Singelton passed to base class constructor leads to no compile time error

I don’t know if this is an known error, but the following code should lead into a compile time error but it doesn’t.

sealed class Base(val value: Base)
object Singleton : Base(Singleton)

fun main(args: Array<String>) {
println(Singleton.value.toString()) // throws NPE
}

Base takes an instance of Base as a parameter (which should be not null) and Singleton is inheriting from it and passes itself (or to be more precisely Singleton.INSTANCE) to the base class constructor, but I guess that instance is not initialized yet so it passes null where null shouldn’t be accepted.

Executing this code leads into a NPE.

Even when the base class is open or abstract it’s still unnoticed by the compiler but fortunately throws an exception at runtime.

I doubt that someone would ever write code like this, but it’s still funny to see that stuff this can happen even though Kotlin is a null safe language.

This code can be compiled to correct code but requires a special case in the compiler. It would be equivalent to object Singleton : Base(this) which is also very fishy.

Also I think this can’t be done without leaking this in the constructor, which is an antipattern (but not actually prohibited, even in Java). So I agree that this shouldn’t be allowed.

1 Like

Well I got curious about this and it’s kinda getting more interesting :sweat_smile::laughing:.

Let’s look at this code:

sealed class Base(val value: Base)
object SingletonOne : Base(SingletonTwo)
object SingletonTwo : Base(SingletonOne)

fun main(args: Array<String>) {
println(SingletonOne.value) // prints SingletonTwo@63947c6b
println(SingletonOne.value.value) // prints null
println(SingletonTwo.value) // prints null
println(SingletonTwo.value.value) // throws NPE
}

Again, no compile errors :disappointed_relieved:

Well, it’s not that much of a surprise that the code behaves this way but it’s still funny how easy it is to fool the compiler :rofl:.

I guess the main issue is that singletons are treated as non-nullable types even when they are not instantiated yet.