Breaking Kotlin's Null-Safety with Circular References

Circular object references can be dangerous. They break code at runtime, and are difficult to guard against at compile time.
Consider the following example:
|
|
Although innocuous at first glance, this code breaks Kotlin’s non-nullable types.
If you run this example on Kotlin Playground, it produces the following output:
|
|
A null
value snuck into a non-nullable type.
How?
Kotlin object
s are lazily initialized1.
LewisHamilton
gets initialized upon its first-access in main
. Its constructor references another lazy variable Mercedes
, whose constructor has a circular reference to LewisHamilton
.
Therefore, constructor of Mercedes
references a yet-uninitialized LewisHamilton
variable, and leads to a null value being stored in a non-nullable variable.
While this is bug is easy to fix, the problem is that this code fails at runtime, thus silently breaking Kotlin’s non-null types.
Consider starring this issue to get more attention to this problem: KT-44633: Circular object references silently break Kotlin’s non-nullable types
More Circular Reference Madness
Let’s look at a few more examples to illustrate problems with circular references.
|
|
This code produces a StackoverflowError
. You can try it here.
println
calls toString()
on foo
. Since Foo
is a data class, its toString()
implementation calls the same method on its member properties too. This leads to an ever growing call-stack.
|
|
A simpler, more condensed version of the same problem can be illustrated as follows:
|
|
This code again produces a StackoverflowError
, as CircularRef.toString()
continues recursing forever. Try it here.
Not just Kotlin
The problem of Circular References plagues other languages too. Here’s an example of the same code in Go.
|
|
Note circular references in the String()
method on both interfaces. Run this here.
This code never prints Finished. While the execution timeout on play.golang.org prevents this code from running for long, the same code on my local machine prints: fatal error: stack overflow
.
Conclusion
Be on the look out for circular references in your Kotlin code. You will receive no compile time errors or warnings about them.
If you would like to change that, consider starring this issue: KT-44634: Circular object references silently break Kotlin’s non-nullable types.
Thanks to Subhrajyoti Sen for reviewing this post!
Question, comments or feedback? Feel free to reach out to me on Twitter @haroldadmin