次のコード
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)
}
出力は期待どおりです。
本当にそこにある列: Tenant_id としての真の
Tenant.id、Tenant_name としての Tenant.name
列
ただし、順序が逆の場合は失敗します。
object RecursiveObjects extends App {
println(Domain.Node.getAliasColumns)
Domain.tables.foreach(t => println(t.getAliasColumns))
}
この場合、出力は
実際に存在する列: Node_id として真の
Node.id、Node_tenantId として Node.tenantId、Node_nodeId として Node.nodeId、Node_name として Node.name
列が実際に存在: false
列 List(null, null)スレッド「メイン」での例外 java.lang.NullPointerException
Scala 2.10.1 を使用
背景情報:
- オブジェクト定義は、RDBMS の論理データ モデルを記述します。
- テーブルはその列 (子) を認識し、各列は自分のテーブル (親) を認識します
- 外部キー列には、親テーブルの主キー列を説明するオプションのプロパティがあります
- この関係は再帰的です (ノード テーブルは再帰的です)。
- テーブルと列の個々の定数が必要です。
- できればvarは避けたい
言語仕様のセクションを見つけました (5.4)
オブジェクト定義によって定義された値は遅延してインスタンス化されることに注意してください。
これは、実際にセットアップできるようにするために必要です。「値」(プロパティ)とは対照的に、「値」はオブジェクト全体を意味すると思います。
とにかく、列プロパティのインスタンスは明らかに作成されていますが、その要素はまだ「具体化」されていません。これは、2 回目の実行の出力に表示されます。
初期の定義を使用して解決しようとしましたが、この場合、コンパイラは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")
[...]
}
}
だから私の質問は:
- なぜ失敗するのですか?
- 列プロパティはどの状態ですか (リストは存在しますが、要素は null です)。
- これを正しく設定するにはどうすればよいですか?または、循環/再帰的な性質のため、回避策として定義された順序で具体化する必要がありますか?
更新/解決策
0__ の回答に基づく: 解決策は、 columns プロパティを遅延として宣言し、抽象 valをdefに変更することで構成されます。
columns プロパティの状態について: scalac のコマンド ライン オプションに-Xcheckinitを指定すると、追加の実行時チェックが追加されます。この場合、次のエラーが表示されます。
原因: scala.UninitializedFieldError: 未初期化フィールド: RecursiveObjects.scala: 35
それ以外の場合、このエラーは黙って無視されるため、リストには null のみが含まれます。