So I started learning Kotlin today and got to Classes when I noticed something weird. When I run this code:
fun main() {
val dog_1 = Dog("Walrek", 7)
}
open class Animal(
open val type: String,
open val name: String,
open val speed: Int,
) {
init {
println("(Ani init) New animal: $type called $name with $speed speed")
}
}
class Dog(
override val name: String,
override val speed: Int,
): Animal("Dog", name, speed) {}
The init method will print out: (Ani init) New animal: Dog called null with 0 speed
.
At first I thought I messed up somewhere and somehow I'm not passing the arguments properly into the Animal
class, but then I added these two lines of code:
init {
println("(Dog init) New animal: $type called $name with $speed speed")
}
into Dog
class, and
println(dog_1.name)
in the main
function, and got this as an output:
(Ani init) New animal: Dog called null with 0 speed
(Dog init) New animal: Dog called Walrek with 7 speed
Walrek
So now my question is, why weren't the parameters passed into the Animal
class, and yet I can access them as usual after creating the Dog
instance?
CodePudding user response:
You should be getting a compiler warning. You should never use open
properties or functions at construction time (in init
blocks or property declarations), because it causes this kind of unwanted behavior. It's just about the only way you can get a NullPointerException in Kotlin without using !!
.
The reason it happens is that the superclass's constructor (which includes init
blocks and property declarations) is run before the subclass. Since you override the property name
in the subclass, the backing field of the property name
has not yet been initialized by the time init
of the superclass is called, so the property returns the default Java field value of null
. In the case of speed
, the field type is a Java primitive int
so its default value is 0.
Note that in your specific example, there was no need to override these properties because you are passing these values to the superconstructor anyway. You could remove the open
keyword before the properties and declare your Dog like:
class Dog(
name: String,
speed: Int,
): Animal("Dog", name, speed) {}
Then it will behave properly.