Summary
I was working with a Kotlin class which extended a Java class and the Kotlin class defined a less-accessible property with the same name as a more-accessible field of the Java class and I ran into an IllegalAccessError
.
It’s now filed as a bug to the Kotlin team at JetBrains.
Background
About Kotlin
Kotlin is a JVM-based language and one of its selling points is that it seamlessly works with any existing Java code. This interoperability of Kotlin lets you start using it without the worry of rewriting or throwing away the millions of lines of Java which your organization has already written and depends on.
Kotlin Properties
Kotlin’s properties feature is a succinct way of defining getters and setters for a variable in a class.
class Foo {
public val fooProperty: String
get() = "Fooooo"
}
Another shortcut available in Kotlin is to define the properties of a class by specifying them in the constructor:
// The Kotlin way
class FooBar(private val foo: String, private val bar: String) { }
// The above is equivalent to this in Java
class FooBar {
private String foo;
private String bar;
public FooBar(String foo, String bar) {
this.foo = foo;
this.bar = bar;
}
}
A bad Cocktail of Kotlin and Java
Now, let’s say you have the following Java class:
class JavaClass {
public String injectedField;
}
and your Kotlin class extended this class as:
class KotlinClass(private val injectedField: String): JavaClass() {
fun printInjectedField(): String {
println(injectedField)
}
}
You would expect the following behavior:
JavaClass
superclass has a field with the same nameinjectedField
asKotlinClass
’s private property,- The Kotlin property will be accessed in
printInjectedField
as there is no usage of thesuper
keyword.
When you run the above code you will be met with an IllegalAccessError
as the printInjectedField
function will try to access the injectedField
value in the Java superclass.
How it was caught
Guice’s Field Injection is Ugly
The setup for the error is contrived and you would only run into in few special cases. One such case is with Guice’s field injection.
The Java class which our Kotlin class extended from was using field injection in many places (which is a code smell). We were passing these dependencies to the Kotlin class via its constructor and we were puzzled whenever we ran into the IllegalAccessError
as we were sure that we were referring to the correct instance.
A Little Help from the Compiler Team
Our first action was to rely on our Googling skills to find something on Stack Overflow and we failed to find anything useful for us. After asking this question in Google’s version of an internal Stack Overflow, we received a response from the people working on the Kotlin compiler at Google and they suggested to rename the property name (i.e. don’t keep the same as the field of the Java superclass).
And that fix worked!
That wasn’t the end of the investigation as they followed up with the JetBrains team to identify the root cause of the issue as they were unsure of the behavior themselves and this is now being tracked as a possible bug in the Kotlin compiler: https://youtrack.jetbrains.com/issue/KT-54393/Change-in-behavior-from-1710-to-1720-for-java-field-override
Lessons
- Java interoperability is good but there might be edge cases which you might unfortunately run into.
- Don’t be afraid to reach out to folks working on Kotlin.
- Don’t use Guice’s field injection if you can avoid it.