L'inizializzazione della variabile di Kotlin per la classe figlio si comporta in modo strano per l'inizializzazione della variabile con valore 0


16

Ho creato la seguente gerarchia di classi:

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

L'output di questo codice è

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

Ma se cambio l'inizializzazione di xda

var x: Int = 33

per

var x: Int = 0

l'output mostra l'invocazione del metodo in contrasto con l'output sopra:

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

Qualcuno sa perché l'inizializzazione con 0causa un comportamento diverso rispetto a quello con un altro valore?


4
Non direttamente correlato, ma chiamare metodi scavalcabili dai costruttori non è generalmente una buona pratica poiché può portare a comportamenti imprevisti (e spezzare efficacemente il contratto / invarianti di superclassi da sottoclassi).
Adam Hošek,

Risposte:


18

la superclasse viene inizializzata prima della sottoclasse.

La chiamata del costruttore di B chiama il costruttore di A, che chiama la funzione f stampando "x in f: 1", dopo che A è inizializzato, il resto di B viene inizializzato.

Quindi, essenzialmente, l'impostazione del valore viene sovrascritta.

(Quando inizializzi le primitive con il loro valore zero in Kotlin, tecnicamente non si inizializzano affatto)

È possibile osservare questo comportamento di "sovrascrittura" modificando la firma da

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

Poiché xnon è più la primitiva int, il campo viene effettivamente inizializzato su un valore, producendo l'output:

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

5
Quando inizializzi le primitive con il loro valore zero in Kotlin, tecnicamente non inizializzano affatto è quello che volevo leggere ... Grazie!
deHaar

Questo sembra ancora un bug / incoerenza.
Kroppeb,

2
@Kroppeb questo è solo Java, lo stesso comportamento può essere osservato solo nel codice Java. Non ha nulla a che fare con Kotlin
Sxtanna il

8

Questo comportamento è descritto nella documentazione - https://kotlinlang.org/docs/reference/classes.html#derived-class-initialization-order

Se una di queste proprietà viene utilizzata nella logica di inizializzazione della classe di base (direttamente o indirettamente, attraverso un'altra implementazione di membro aperto sovrascritta), può portare a un comportamento errato o a un errore di runtime. Quando si progetta una classe di base, è pertanto necessario evitare di utilizzare membri aperti nei costruttori, inizializzatori di proprietà e blocchi init.

UPD:

C'è un bug che produce questa incoerenza - https://youtrack.jetbrains.com/issue/KT-15642

Quando una proprietà viene assegnata come effetto collaterale di una chiamata di funzione virtuale all'interno del super costruttore, il suo inizializzatore non sovrascrive la proprietà se l'espressione dell'inizializzatore è il valore predefinito del tipo (null, zero primitivo).


1
Inoltre, IntelliJ ti avverte al riguardo. La chiamata f()nel initblocco di Adà l'avviso "Chiamata della funzione non finale f nel costruttore"
Kroppeb

Nella documentazione fornita, si dice che "l'inizializzazione della classe base viene eseguita come primo passo e quindi avviene prima dell'esecuzione della logica di inizializzazione della classe derivata", che è esattamente ciò che accade nel primo esempio nella domanda. Tuttavia nel secondo esempio l'istruzione di inizializzazione ( var x: Int = 0) della classe derivata non viene eseguita affatto, il che è contrario a ciò che dice la documentazione che mi porta a credere che questo potrebbe essere un bug.
Subaru Tashiro,

@SubaruTashiro Sì, hai ragione. È un altro problema: youtrack.jetbrains.com/issue/KT-15642 .
vanyochek,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.