Companion Objects Are Real Objects Too

Amanda Hill

For as long as there have been Android devices, there have been Android developers programming in Java. Or at least, that was the case until 2011 when Jetbrains announced they were developing a new statically typed programming language for the JVM. And if it’s good enough for the JVM then it’s good enough for Android!

But why build a new language when Java is so dandy?

“[We] believe that the community can benefit from a new statically typed JVM-targeted language free of the legacy trouble and having the features so desperately wanted by the developers.”

While Kotlin does make good on its promise to offer Android developers a modern language, it is not all sunshine and roses. Kotlin giveth, and it also taketh away. One example is static constants. In Java, classes can have static constants - fields that are only created once per class (as opposed to once per object), cannot be changed (without recompiling), and can be accessed without an instance of their containing class. Static constants are a great tool for developers as they allow us to share the same variable (both in value and in memory location) across all instances of a given class. While the memory storage is a nice bonus, the real advantage is in their ability to encode meaning into an otherwise random variable. Consider the following:

private static final int MAX_ALPHA = 255;

Because the static constant we are defining is a primitive type, the compiler will replace our constant name everywhere with its value, in this case 255, at compile time. So there is absolutely no reason for us to not just write 255 everywhere we use that constant. Well, no reason, except that 255 on it’s own is a magic number which someone else, might, very reasonably, not know happens to be the maximum value representable by an eight-digit binary number and therefore represents the maximum value that can be assigned to elements in the 24-bit RGBA color model, since each color channel is allotted eight bits. But I digress.

All of that (totally) unnecessary (but hopefully informative) mathematic malarky was to make the point that static constants are valuable and can be very helpful in making code more readable.

But why does any of that matter in terms of Kotlin? Well in Kotlin, we don’t have static constants anymore.

The official Kotlin documentation recommends the following alternatives:

If you need to write a function that can be called without having a class instance … you can write it as a member of an object declaration inside that class.

Even more specifically, if you declare a companion object inside your class, you’ll be able to call its members with the same syntax as calling static methods in Java/C#, using only the class name as a qualifier.

If you read that and thought, “companion objects are for me! They’ll make my Kotlin code can look just like my Java code and, just as Bob Marley predicted, ‘every little thing gonna be alright’!” Then you are just like me. And sadly, just like me, you would be wrong. Every little thing not gonna be alright. While companion objects do offer us the same syntax as our dear Java static constants, they are not the same thing.

Consider how our earlier static constant would look in Kotlin:

companion object {
  private val MAX_ALPHA: Int = 255
}

Looks pretty similar right?. Well, as the title of this blog post suggests, companion objects are objects too. Which means that to get at that MAX_ALPHA we would actually have to create an object first, then access it’s member variable. And object creation is expensive. And let me tell you, that integer is not worth the cost. How do I know? Because my Android trace profile told me of course!

Don’t believe me? Let’s take a look at some profiles. Consider a custom view that uses our MAX_ALPHA value on each onDraw call.

Here’s what the profile looks if we use a companion object:

I have highlighted the row fetching the MAX_ALPHA variable from the companion object class.

Now here is what the trace looks like if we just place the MAX_ALPHA as just a class variables:

You will notice that total CPU Time % for the same drawDiagonalLines method went from 18.7% with a companion object, to 11.1% without it! Do you believe me now?

For the purposes of this exmaple, replacing a companion object with a class variable was an acceptable solution, but if that won’t work for you, and you need a constant with a broader scope, you can use package-level functions.

Regardless of what soltuion you go with, as long as you remember that companion objects are objects too, and not the same as Java’s static constants, then, maybe, just maybe, “every little thing is gonna be alright”