Kotlin Object Declarations – The fake-static

Instead of implementing my own backup application as I had planned a long time ago, I’m wandering off (re)learning Kotlin after a long absence from that language. In my defense though, I’m doing it in the context of the backup app which will not be Java as originally intended (or maybe later for comparison, who knows, I obviously can’t be trusted with my plans). Putting that aside, the most confusing concept of Kotlin for a Java developer is the object. What is that thing doing that a class can’t do and how do we declare static fields and methods? I know it’s nothing new, but that part seems to have changed a bit since I used Kotlin about two (?) years ago. So, for me this is a refresh of old information and also something new and by writing about it I will engrave it in my brain once and for all. And your confusion will hopefully turn into some productive… fusion… of some sort… or so.

Where’s the Java static?

The first question a Java developer coming to Kotlin probably needs answered is how to declare a static variable or function. This will be the first time you come in contact with object or, more specifically, the companion object. Let me show you a totally artificial Java snippet and how it would be implemented in Kotlin.

Java

class ArtificialExampleJ {
    static final int THE_ANSWER = 42;
}

// Usage from Java
@Test
public void test_static_member() {
    assertThat(ArtificialExampleJ.THE_ANSWER).isEqualTo(42);
}

// Usage from Kotlin
@Test
fun test_java_artificial_static() {
    assertThat(ArtificialExampleJ.THE_ANSWER).isEqualTo(42)
}

Kotlin

// Declaration
class ArtificialObjectDeclarations {
    companion object {
        const val THE_ANSWER = 42
    }
}

// Usage from Kotlin
@Test 
fun test_companion_object() {
    assertThat(ArtificialObjectDeclarations.THE_ANSWER).isEqualTo(42)
}

// Usage from Java
@Test
public void test_kotlin_artificial_companion_object() {
    assertThat(ArtificialObjectDeclarations.THE_ANSWER).isEqualTo(42);
}

What is important to understand is the fact that Kotlin really creates an object here. But since it is a companion object it can be referenced by the parent class’ object. Were you to remove the companion from the object (every time I read or write Companion I am reminded of the Firefly TV series) then you have to give it a name and also reference the field through that name.

// Declaration
class ArtificialObjectDeclarations {
    object NamedObject {
        const val NAMED_ANSWER = 42
    }
}

// Usage from Kotlin
@Test
fun test_named_object() {
    assertThat(ArtificialObjectDeclarations.NamedObject.NAMED_ANSWER)
        .isEqualTo(42)
}

// Usage from Java
@Test
public void test_kotlin_artificial_named_object() {
    assertThat(ArtificialObjectDeclarations.NamedObject.NAMED_ANSWER)
            .isEqualTo(42);
}

Such objects are singletons and are not required to be contained in a class. Kotlin is much more relaxed than Java in this regard. You can declare multiple classes or objects in the same file.

// Declaration
class ArtificialObjectDeclarations {

    companion object {
        const val THE_ANSWER = 42
    }

    object NamedObject {
        const val NAMED_ANSWER = 42
    }
}

object ArtificialSingleton {
    private var c = 0

    fun tellMeTheValue() : Int {
        // I even managed to get some C++ into Kotlin.
        return c++
    }
}

// Usage
@Test
fun test_singleton() {
    assertThat(ArtificialSingleton.tellMeTheValue()).isEqualTo(0)
    assertThat(ArtificialSingleton.tellMeTheValue()).isEqualTo(1)
}

Here’s the Java equivalent as decompiled from Bytecode. I have to admit that I was pretty surprised when I saw the outcome.

// ArtificialObjectDeclaration.class
public final class ArtificialObjectDeclarations {
   public static final int THE_ANSWER = 42;
   public static final com.thecodeslinger.ArtificialObjectDeclarations.Companion Companion = new com.thecodeslinger.ArtificialObjectDeclarations.Companion((DefaultConstructorMarker)null);
}

// ArtificialObjectDeclarations$Companion.class
public final class ArtificialObjectDeclarations$Companion {
   private ArtificialObjectDeclarations$Companion() {
   }

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

// ArtificialObjectDeclarations$NamedObject.class
public final class ArtificialObjectDeclarations$NamedObject {
   public static final int NAMED_ANSWER = 42;
   public static final ArtificialObjectDeclarations$NamedObject INSTANCE;

   private ArtificialObjectDeclarations$NamedObject() {
   }

   static {
      ArtificialObjectDeclarations$NamedObject var0 = new ArtificialObjectDeclarations$NamedObject();
      INSTANCE = var0;
   }
}

The compiler creates the statics as a standalone field instead of the (companion)object. So what looks natural in Kotlin looks like a waste of memory in Java because you have the static final int and an instance of the object.

Digging a bit deeper I found the following information in the Kotlin docs (section "Static fields").

Properties declared as const (in classes as well as at the top level) are turned into static fields in Java

Even though object is not mentioned here, it explains what is happening. The examples in the docs show objects though, so I’m not interpreting something into this sentence. Note that this behavior is different for functions. Section "Static methods" of the previously linked documentation page explains that those have to be called through an instance of the object from Java unless the function is annotated with @JvmStatic.

Take a look at this Kotlin code.

class ArtificialObjectDeclarations {
    companion object {
        const val THE_ANSWER = 42
        fun companionFunction() {
            return THE_ANSWER
        }
    }
}

It is turned into this Java equivalent.

// ArtificialObjectDeclarations.class
public final class ArtificialObjectDeclarations {
   public static final int THE_ANSWER = 42;
   public static final com.thecodeslinger.ArtificialObjectDeclarations.Companion Companion = new com.thecodeslinger.ArtificialObjectDeclarations.Companion((DefaultConstructorMarker)null);
}

// ArtificialObjectDeclarations$Companion.class
public final class ArtificialObjectDeclarations$Companion {
   public final void companionFunction() {
       return 42;
   }

   private ArtificialObjectDeclarations$Companion() {
   }

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

As you can see, the function (now called a method) only exists on the Companion class and as such has to be called this way in Java.

@Test
public void test_kotlin_compaion_function() {
    assertThat(ArtificialObjectDeclarations.Companion.companionFunction())
            .isEqualTo(42);
}

All of these examples are called "Object Declaration". Kotlin also has a concept of "Object Expression" which I’ll handle in another post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.