There has recently been a discussion online about the best approach for storing global constants, or public static final fields in Javaspeak. This short article describes available options and points out some of the pitfalls you can run into. But before we start, let’s talk a bit about decompiling Kotlin code.
A companion object is initialized when the class is loaded (typically the first time it’s referenced by other code that is being executed), in a thread-safe manner. You can omit the name, in which case the name defaults to Companion. A class can only have one companion object, and companion objects can not be nested.
Companion objects and their members can only be accessed via the containing class name, not via instances of the containing class. Kotlin does not support class-level functions that also can be overridden in subclasses (like @classmethod in Python). If you try to redeclare a companion object in a subclass, you’ll just shadow the one from the base class. If you need an overridable “class-level” function, make it an ordinary open function in which you do not access any instance members – you can override it in subclasses, and when you call it via an object instance, the override in the object’s class will be called. It is possible, but inconvenient, to call functions via a class reference in Kotlin, so we won’t cover that here.
If you’ve used Kotlin for some time you should’ve noticed that the language is a wonderful boilerplate reduction tool. Kotlin makes it easy to express complex things with simple code, with compiler doing the dirty work. A great example is the data class feature, which allows you to easily substitute tens of lines of Java code with just a single line of Kotlin. However, as we all know, with great power comes great responsibility.
It’s not hard to make Kotlin compiler produce suboptimal bytecode, and, especially if you’re doing Kotlin for Android, you have to be aware of the number of classes, methods and object allocations that your code will produce. Luckily, JetBrains has us covered with a decompiler tool integrated into Android Studio (and IntelliJ IDEA of course), that helps examine the bytecode, and even produces similar Java code. The latter makes it easy to optimize Kotlin.
Sometimes we need to create an object of a slight modification of some class, without explicitly declaring a new subclass for it. Kotlin handles this case with object expressions and object declarations.