子クラスのKotlin変数の初期化は、値0で変数を初期化するために奇妙な動作をします


16

次のクラス階層を作成しました:

open class A {
    init {
        f()
    }

    open fun f() {
        println("In A f")
    }
}

class B : A() {
    var x: Int = 33

    init {
        println("x: " + x)
    }

    override fun f() {
        x = 1
        println("x in f: "+ x)
    }

    init {
        println("x2: " + x)
    }
}

fun main() {
    println("Hello World!!")
    val b = B()
    println("in main x : " + b.x)
}

このコードの出力は

Hello World!!
x in f: 1
x: 33
x2: 33
in main x : 33

しかしxからの初期化を変更した場合

var x: Int = 33

var x: Int = 0

上記の出力とは対照的に、出力はメソッドの呼び出しを示しています。

Hello World!!
x in f: 1
x: 1
x2: 1
in main x : 1

での初期化0が別の値での初期化と異なる動作を引き起こす理由を誰かが知っていますか?


4
直接関係はありませんが、コンストラクターからオーバーライド可能なメソッドを呼び出すことは、予期しない動作(およびスーパークラスコントラクト/サブクラスからの不変式を実質的に壊すこと)につながる可能性があるため、通常はお勧めできません。
アダムHošek

回答:


18

スーパークラスはサブクラスの前に初期化されます。

Bのコンストラクター呼び出しは、Aのコンストラクターを呼び出します。Aのコンストラクターは、「x in f:1」を出力する関数fを呼び出します。Aが初期化された後、残りのBが初期化されます。

したがって、基本的に、値の設定は上書きされます。

(Kotlinでプリミティブをゼロ値で初期化すると、技術的にはまったく初期化されません)

この「上書き」動作は、署名を

var x: Int = 0var x: Int? = 0

xはプリミティブintではなくなったため、フィールドは実際には値に初期化され、出力が生成されます。

Hello World!!
x in f: 1
x: 0
x2: 0
in main x : 0

5
Kotlinでプリミティブをゼロ値で初期化すると、技術的にまったく初期化されないプリミティブが読みたかったのですが、ありがとう!
deHaar

これはまだバグ/不整合のようです。
Kroppeb

2
@Kroppebこれは単なるJavaです。Javaコードだけでも同じ動作が見られます。Kotlinとは何の関係もありません
Sxtanna

8

この動作はドキュメントで説明されています— https://kotlinlang.org/docs/reference/classes.html#deriv-class-initialization-order

これらのプロパティのいずれかが基本クラスの初期化ロジックで(直接または間接的に、別のオーバーライドされたオープンメンバー実装を介して)使用されている場合、不正な動作またはランタイムエラーが発生する可能性があります。したがって、基本クラスを設計するときは、コンストラクター、プロパティ初期化子、およびinitブロックでオープンメンバーを使用しないようにする必要があります。

UPD:

この不整合を引き起こすバグがありますhttps://youtrack.jetbrains.com/issue/KT-15642

スーパーコンストラクター内の仮想関数呼び出しの副作用としてプロパティが割り当てられている場合、初期化子式がデフォルト値(null、プリミティブゼロ)の場合、その初期化子はプロパティを上書きしません。


1
さらに、IntelliJはそれについて警告します。f()initブロックで呼び出すと、A「コンストラクターで非最終関数fを呼び出す」という警告が表示されます
Kroppeb

あなたが提供したドキュメントでは、「基本クラスの初期化は最初のステップとして行われるため、派生クラスの初期化ロジックが実行される前に発生します」とあり、これは問題の最初の例で正確に発生します。ただし、2番目の例var x: Int = 0では、派生クラスの初期化命令()はまったく実行されません。これは、ドキュメントに記載されている内容に反しており、これがバグである可能性があると私に思わせます。
田代スバル

@SubaruTashiroはい、そうです。これは別の問題です— youtrack.jetbrains.com/issue/KT-15642
vanyochek
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.