L'initialisation de la variable Kotlin pour la classe enfant se comporte bizarrement pour l'initialisation de la variable avec la valeur 0

16

J'ai créé la hiérarchie de classes suivante:

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)
}

La sortie de ce code est

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

Mais si je change l'initialisation à xpartir

var x: Int = 33

à

var x: Int = 0

la sortie montre l'invocation de la méthode contrairement à la sortie ci-dessus:

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

Est-ce que quelqu'un sait pourquoi l'initialisation avec 0provoque un comportement différent de celui avec une autre valeur?

PRATYUSH SINGH
la source
4
Pas directement lié, mais appeler des méthodes remplaçables à partir de constructeurs n'est généralement pas une bonne pratique car cela peut conduire à un comportement inattendu (et rompre efficacement le contrat / invariants de la superclasse des sous-classes).
Adam Hošek

Réponses:

18

la super classe est initialisée avant la sous-classe.

L'appel du constructeur de B appelle le constructeur de A, qui appelle la fonction f en imprimant "x dans f: 1", après que A est initialisé, le reste de B est initialisé.

Donc, essentiellement, le réglage de la valeur est écrasé.

(Lorsque vous initialisez des primitives avec leur valeur nulle dans Kotlin, elles ne se initialisent techniquement pas du tout)

Vous pouvez observer ce comportement de "remplacement" en modifiant la signature de

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

Depuis xn'est plus le primitifint , le champ est réellement initialisé à une valeur, produisant la sortie:

Hello World!!
x in f: 1
x: 0
x2: 0
in main x : 0
Sxtanna
la source
5
Lorsque vous initialisez des primitives avec leur valeur nulle dans Kotlin, techniquement, elles ne s'initialisent pas du tout, c'est ce que je voulais lire ... Merci!
deHaar
Cela ressemble toujours à un bug / incohérence.
Kroppeb
2
@Kroppeb c'est juste Java, le même comportement peut être observé dans le code Java seul. Cela n'a rien à voir avec Kotlin
Sxtanna
8

Ce comportement est décrit dans la documentation - https://kotlinlang.org/docs/reference/classes.html#derived-class-initialization-order

Si l'une de ces propriétés est utilisée dans la logique d'initialisation de la classe de base (directement ou indirectement, via une autre implémentation de membre ouvert surchargée), cela peut entraîner un comportement incorrect ou une défaillance d'exécution. Lors de la conception d'une classe de base, vous devez donc éviter d'utiliser des membres ouverts dans les constructeurs, les initialiseurs de propriété et les blocs init.

UPD:

Il y a un bogue qui produit cette incohérence - https://youtrack.jetbrains.com/issue/KT-15642

Lorsqu'une propriété est affectée en tant qu'effet secondaire d'un appel de fonction virtuelle à l'intérieur du super constructeur, son initialiseur ne remplace pas la propriété si l'expression de l'initialiseur est la valeur par défaut du type (null, zéro primitif).

vanyochek
la source
1
De plus, IntelliJ vous en avertit. L'appel f()dans le initbloc de Adonne l'avertissement "Appel de la fonction non finale f dans le constructeur"
Kroppeb
Dans la documentation que vous avez fournie, il est indiqué que "l'initialisation de la classe de base est effectuée comme première étape et se produit donc avant l'exécution de la logique d'initialisation de la classe dérivée", ce qui est exactement ce qui se produit dans le premier exemple de la question. Cependant, dans le deuxième exemple, l'instruction d'initialisation ( var x: Int = 0) de la classe dérivée n'est pas exécutée du tout, ce qui est contraire à ce que dit la documentation, ce qui m'amène à penser que cela pourrait être un bogue.
Subaru Tashiro
@SubaruTashiro Oui, vous avez raison. C'est un autre problème - youtrack.jetbrains.com/issue/KT-15642 .
vanyochek