0

I want to know why exactly my code returns "nullnullnull" instead of expected answer and why does it work correctly when method is used.

Consider the following code:

open class Base( open val a: String, open val b: String, open val c: String, ) { val concatenation = a + b + c } class Derived( override val a: String, override val b: String, override val c: String ) : Base(a = a, b = b, c = c) fun main() { val derived = Derived(a = "foo", b = "bar", c = "baz") println(derived.concatenation) } 

This example prints out "nullnullnull", instead of "foobarbaz".

Though if you replace val concatenation = a + b + c with fun concatenation() = a + b + c in the superclass it seems to work just fine with that method call.

Also, IDEA warns of Accessing non-final property <property> in constructor when using val concatenation = a + b + c, but I'm not sure what exactly does this mean.

Is it something with the order of which Base and Derived classes properties initialized? I thought that it might be that I use concatenation from the Base class, but with inheritance I call Base class constructor too and the same properties with same strings must be initialized.

3
  • Does this answer your question? Kotlin calling non final function in constructor works Commented Jun 15, 2024 at 13:40
  • 3
    @Alexander While the direct cause of both is that the base class calls something that the derived class overrides, it is not as obvious in this question, IMO. Commented Jun 15, 2024 at 13:51
  • @Alexander the big difference that I see is that my code doesn't throw NPE (even when all properties are non-nullable), and also I don't override concatenation in the derived class. From my understanding both Base Object and Derived Object constructed should have all data regarding properties a, b and c, because they are directly passed to the both constructors. Commented Jun 15, 2024 at 15:29

2 Answers 2

2

In my opinion, they should have designed Accessing non-final property <property> in constructor as a compile error, not just a warning, since it creates weird behaviors that are not necessarily even defined in the documentation. For instance, you can easily create a NullPointerException without using !! or any Java interoperational code by doing this.

"Non-final" means the same thing as "open". You are calling open properties or functions from a constructor, which can create big issues and should never be done.

The code that assigns the initial values of properties is considered part of the constructor, so your code = a + b + c is part of your constructor and is what triggers the warning.

Here's what is happening to get the nulls. The Derived class constructor passes the three values to the super class constructor. The super class assigns these values to the super class's backing fields of its a, b, and c properties. Then concatenation is calling the a, b, and c properties to get their values, but since we are actually an instance of Derived, these properties have been overridden and are pointing to different backing fields than the ones that were initialized as part of the super class.

Also, since we are still executing the super class constructor, the backing fields of the derived class have not been initialized yet, so they still hold null values.

Sign up to request clarification or add additional context in comments.

1 Comment

Interesting,. Thank you for the answer, I didn't expect for properties to be null before initialization, I thought that Kotlin fool-proofs every aspect of Java inherent nullability.
2

Since you have overriden a, b, c; why on earth should your Base-Class know the value of the overriden fields?

I assume, you would like to parameterice a, b, c instead of overriding them.

open class Base( val a: String, val b: String, val c: String, ) { val concatenation = a + b + c } class Derived( a: String, b: String, c: String ) : Base(a = a, b = b, c = c) fun main() { val derived = Derived(a = "foo", b = "bar", c = "baz") println(derived.concatenation) } 

1 Comment

That's good solution too, thanks,