Kotlin and static, not as easy as Java

Daniele Bottillo
3 min readMar 12, 2018

--

If you write Android code, it is very likely that you have some static field to use as key of your Shared Preferences or Bundles and usually in Java they are defined statically:

public class MyAwesomeActivity {
public static final String KEY = "MyAwesomeKey";
}

If you try to convert this code automatically in Kotlin from Android Studio/IntelliJ, it will generate this:

class MyAwesomeActivity {
companion object {
val KEY = "MyAwesomeKey"
}
}

So, for Android Studio/IntelliJ every static field within a class gets converted into a field or a method of the companion object. The companion object is a sort of utility for a class and if you look at the documentation it says:

Note that, even though the members of companion objects look like static members in other languages, at runtime those are still instance members of real objects, and can, for example, implement interfaces.

Wait...what? Our static field is now within an instance member? If you look at the decompiled bytecode (Tools → Kotlin → Show Kotlin bytecode → Decompile) you can understand what’s going on:

public final class MyAwesomeActivity {
@NotNull
private static final String KEY = "MyAwesomeKey";
public static final MyAwesomeActivity.Companion Companion = new MyAwesomeActivity.Companion((DefaultConstructorMarker)null);

public static final class Companion {
@NotNull
public final String getKEY() {
return MyAwesomeActivity.KEY;
}

private Companion() {
}

// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}

That’s a lot of code! I’ve removed the metadata just for clarity, but fundamentally our field is now private within the class and there is a static class instance with a getKey() method to access the field, which seems quite a complicated solution! I personally think that Kotlin companion objects are very powerful but they’re not made for just exposing a static field.

So, are there any other options? Yes, there are! You can either declare the field with const in the companion object or put the field outside the class definition without any companion object:

// MyAwesomeActivity.kt file
const val STATIC_FIELD
= "MyAwesomeKey1"

class
MyAwesomeActivity{
companion object{
const val INNER_STATIC_FIELD = "MyAwesomeKey2"
}
}

Now if you look at the decompiled bytecode:

public final class MyAwesomeActivityKt {
@NotNull
public static final String STATIC_FIELD = "MyAwesomeKey1";
}
public final class MyAwesomeActivity {
@NotNull
public static final String INNER_STATIC_FIELD = "MyAwesomeKey2";
public static final MyAwesomeActivity.Companion Companion = new MyAwesomeActivity.Companion((DefaultConstructorMarker)null);

public static final class Companion {
private Companion() {
}

// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}

So it’s looking better now! Although there is still a companion object, the INNER_STATIC_FIELD now it’s public static final and can be accessed without the companion object instance, which is now completely useless! On the other hand, STATIC_FIELD looks exactly our initial java code: it is defined in a class just as public static final even though the class is not “our” class but MyAwesomeActivityKt.java which is how Kotlin handles having a field declaration outside the MyAwesomeActivity class. That makes possible to have classes that don’t match the name of the file.

In conclusion, if you just need a static field I would suggest to declare the field as const val outside the class to avoid having a companion object and the related inner static class, alternatively just remember to use const inside the companion object to at least avoid the getter method.

Happy Kotlin! :)

--

--

Daniele Bottillo

Android@Monzo Bank (London), technology enthusiast, book reader, TV shows fan, game player, Lego addicted.