Disclaimer: I am fairly new to the language, so if null invariance is not considered a strict property of the language, or if there is some other Kotlin “rule” I am breaking here please let me know.
I believe I have found an easily reproducible way to break Kotlin’s nullability invariance by utilizing constructor parameter member initialization syntax. As an example, please see below
fun main() {
// Instantiate with some string
SubTest("Hello World")
}
// nonNullString is not initialized until after superclass initialization is complete
class SubTest(private val nonNullString: String): Test() {
override fun doSomething() {
// Since nonNullString is not a nullable type, this should never throw
checkNotNull(nonNullString)
}
}
// Superclass calls the subclass' implementation of a function during its initialization
// (any thing wrong with this?)
abstract class Test {
private val takeSomething = doSomething()
abstract fun doSomething()
}
Output:
Exception in thread “main” java.lang.IllegalStateException: Required value was null.
While the usage pattern is rare, in my opinion it is far from unacceptable and I ran into this naturally in my own project.
Regardless, this is only possible thanks to the ability to declare class members inside the constructor signature and I see it as an easy way to break null type invariance.