以下代码
abstract class Table(val name: String) {
val columns: List[Column]
def getAliasColumns: String = {
val reallyThere = columns.forall(c => c != null)
println("Columns really there: " + reallyThere)
if (reallyThere == false) {
println(" columns " + columns)
}
columns.map(c => s"${c.table.name}.${c.name} as ${c.table.name}_${c.name}")
.mkString(", ")
}
}
class Column(val table: Table, val name: String, val foreignKey: Option[Column])
object Column {
def apply(table: Table, name: String): Column = {
new Column(table, name, foreignKey = None)
}
def apply(table: Table, name: String, fk: Column): Column = {
new Column(table, name, Some(fk))
}
}
object Domain {
object Tenant extends Table("Tenant") {
object Columns {
// Primary key
val Id = Column(Tenant, "id")
// Just a name
val Name = Column(Tenant, "name")
}
val columns = List(Columns.Id, Columns.Name)
}
object Node extends Table("Node") {
object Columns {
// Primary key
val Id = Column(Node, "id")
// Foreign key to another table
val TenantId = Column(Node, "tenantId", Tenant.Columns.Id)
// Foreign key to itself
val NodeId = Column(Node, "nodeId", Id)
// Just a name
val Name = Column(Node, "name")
}
val columns = List(Columns.Id, Columns.TenantId,
Columns.NodeId, Columns.Name)
}
val tables = List(Tenant, Node)
}
有效,如果访问信息的顺序是:
object RecursiveObjects extends App {
Domain.tables.foreach(t => println(t.getAliasColumns))
println(Domain.Node.getAliasColumns)
}
并且输出如预期:
真正存在的列:true
Tenant.id 作为 Tenant_id,Tenant.name 作为 Tenant_name
真正存在的列:true
Node.id 作为 Node_id,Node.tenantId 作为 Node_tenantId,Node.nodeId 作为 Node_nodeId,Node.name 作为 Node_name
但如果顺序颠倒,它会失败:
object RecursiveObjects extends App {
println(Domain.Node.getAliasColumns)
Domain.tables.foreach(t => println(t.getAliasColumns))
}
在这种情况下,输出是
真正存在的列:true
Node.id 作为 Node_id,Node.tenantId 作为 Node_tenantId,Node.nodeId 作为 Node_nodeId,Node.name 作为 Node_name
真正存在的列:假
列 List(null, null)线程“主”java.lang.NullPointerException 中的异常
使用 Scala 2.10.1
一些背景资料:
- 对象定义描述了 RDBMS 的逻辑数据模型。
- 表知道他们的列(子),每个列都知道他的表(父)
- 外键列具有描述父表中主键列的可选属性
- 这种关系可以是递归的(节点表是递归的)
- 表和列的单独常量是必需的。
- 如果可能的话,我想避免var
我在语言规范(5.4)中找到了一个部分
请注意,对象定义定义的值是惰性实例化的。
这实际上是能够设置它所必需的。我假设“价值”是指整个对象,与其“价值”(属性)相反。
无论如何,显然创建了 columns 属性的实例,但它的元素尚未“物化”,这在第二次运行的输出中可见。
我曾尝试使用早期定义来解决它,但在这种情况下,编译器会抱怨涉及 object 的非法循环引用,因此无法编译:
object Node extends {
val columns = List(Domain.Node.Columns.Id, Domain.Node.Columns.TenantId,
Domain.Node.Columns.NodeId, Domain.Node.Columns.Name)
} with Table("Node") {
object Columns {
// Primary key
val Id = Column(Node, "id")
[...]
}
}
所以我的问题是:
- 为什么会失败?
- columns 属性处于哪个状态(列表存在,但元素为空)?
- 如何正确设置?或者由于它的循环/递归性质,我应该按照定义的顺序将它实现为一种解决方法吗?
更新/解决方案
基于 0__ 的回答:解决方案包括将 columns 属性声明为惰性并将抽象 val更改为def。
至于 columns 属性的状态:如果将-Xcheckinit放入 scalac 的命令行选项中,则会添加额外的运行时检查。在这种情况下,会出现以下错误:
引起:scala.UninitializedFieldError:未初始化字段:RecursiveObjects.scala:35
否则此错误会被静默忽略,因此列表仅包含空值。