Kotlinでは、各クラスは1つのプライマリコンストラクタ(主要なコンストラクタ)と複数のセカンダリコンストラクタ(副コンストラクタ)を持つことができます。コンストラクタデリゲーションは、セカンダリコンストラクタが必ずプライマリコンストラクタを直接呼び出すか、同じクラスの他のセカンダリコンストラクタを呼び出す(最終的にはプライマリに)メカニズムです。クラスが他のクラスを継承する場合、各セカンダリコンストラクタは、必要に応じてスーパークラスのコンストラクタを明示的にデリゲートする必要があります。
Javaでは、コンストラクタはthis()やsuper()を使用して相互に直接呼び出すことができ、さまざまな組み合わせでコンストラクタをオーバーロードできます。Kotlinではこの概念が形式化されており、クラスには1つのプライマリコンストラクタのみが存在でき、副コンストラクタはデリゲーションのロジックを使用する必要があることが明示されている必要があります。
デリゲーションの不適切な実装はコンパイルエラーを引き起こす可能性があります。もしプライマリコンストラクタが宣言されている場合、呼び出さないことはできません。また、継承されたクラスでスーパークラスのコンストラクタが存在しない場合、スーパークラスのコンストラクタを呼び出さないこともできません。initブロックがどのタイミングで呼び出されるか、パラメータがどのように渡されるか、およびデリゲーションが初期化の順序にどのように影響するかを理解することが重要です。
基本的なデリゲーションの例:
class Person(val name: String) { constructor(name: String, age: Int) : this(name) { println("セカンダリコンストラクタ: $name, $age") } }
継承を使用する場合:
open class Parent(val name: String) class Child : Parent { constructor(name: String) : super(name) { println("子供のセカンダリ: $name") } }
セカンダリコンストラクタはデリゲートせずに何も呼び出さないことができますか?
いいえ、コンパイラはthis(...)またはsuper(...)を明示的に呼び出すよう要求します。さもなければエラーが発生します。
セカンダリコンストラクタを使用する場合、初期化とinitブロックはどのような順序で実行されますか?
最初に常にプライマリコンストラクタおよびinitブロックが呼び出され、その後でセカンダリコンストラクタの初期化コードが実行されます。
class Demo(val value: String) { init { println("initブロック") } constructor(value: String, code: Int) : this(value) { println("セカンダリ: $code") } } Demo("kotlin", 7) // 出力: // initブロック // セカンダリ: 7
子クラスは親のセカンダリコンストラクタがあっても、親のプライマリコンストラクタだけを呼び出すことができますか?
はい、ただし明示的にsuper(...)を介して行う必要があります。セカンダリは子クラスには直接見えません。
プロジェクトでセカンダリコンストラクタがプライマリを呼び出さず、必要な初期化(たとえば、必須フィールドの設定)が行われないため、バグやランタイムのクラッシュを引き起こすことがあります。
長所:
短所:
すべてのセカンダリコンストラクタがthis(...)を介して厳密にデリゲートされ、必要な初期化がプライマリ/initで集中管理され、構造が保守にとって透明になります。
長所:
短所: