下面的代碼對象中值的初始化順序:如何正確設置循環/遞歸對象?
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)
}
和如預期的輸出:
Columns really there: true
Tenant.id as Tenant_id, Tenant.name as Tenant_name
Columns really there: true
Node.id as Node_id, Node.tenantId as Node_tenantId, Node.nodeId as Node_nodeId, Node.name as Node_name
,但失敗了,如果順序顛倒:
object RecursiveObjects extends App {
println(Domain.Node.getAliasColumns)
Domain.tables.foreach(t => println(t.getAliasColumns))
}
,並在這種情況下,輸出是
Columns really there: true
Node.id as Node_id, Node.tenantId as Node_tenantId, Node.nodeId as Node_nodeId, Node.name as Node_name
Columns really there: false
columns List(null, null)Exception in thread "main" java.lang.NullPointerException
使用Scala的2.10.1
一些背景資料:
- 對象定義描述RDBMS的邏輯數據模型。
- 表瞭解自己的列(兒童),每列都知道他的表(母公司)
- 的外鍵列有一個可選屬性描述父表的主鍵列
- 這種關係可以遞歸(節點表是遞歸的)
- 表和列的單個常量是必需的。
- 如果可能的話,我想,以避免VAR
我發現在實際需要能夠設置的語言規範(5.4)
Note that the value defined by an object definition is instantiated lazily.
節它根本就沒有。我認爲「價值」是指作爲一個整體的對象,而不是它的「價值」(屬性)。
無論如何,顯然會創建columns屬性的實例,但其元素尚未「實現」,這在第二次運行的輸出中可見。
我曾嘗試使用早期的定義解決這個問題,但在這種情況下,編譯器涉及對象非法循環參考抱怨,所以這並不編譯:
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")
[...]
}
}
所以我的問題是:
- 它爲什麼失敗?
- 在哪些狀態是列屬性(該列表存在,但元素爲空)?
- 如何正確設置?還是應該將其按照定義的順序實現爲解決方法,因爲它具有循環/遞歸性質?
更新/溶液
基於0 __的回答是:該解決方案由聲明列屬性作爲懶惰和改變抽象VAL成DEF的。
至於列屬性的狀態:如果您將-Xcheckinit放入scalac的命令行選項中,則會添加其他運行時檢查。在這種情況下,出現以下錯誤:
Caused by: scala.UninitializedFieldError: Uninitialized field: RecursiveObjects.scala: 35
此錯誤以其他方式默默忽略,因此列表僅包含空值。
非常感謝您的回答,非常有幫助。我已將結果添加到問題中。 – Beryllium