
A true companion: exploring Kotlin’s companion objects
For the past year of my Kotlin study and development I used companion objects only for the purpose of storing static functions and properties.
But recently I decided to sort all the things out for myself about this simple at the first look language’s structure. If you’re interested in what you can do with this tool, let’s dive in.
Basics
So, what exactly companion object
do? It’s pretty simple. It creates a static inner class and gives access to it’s single instance. Basically, it’s a singleton class with properties and functions inside the defining class.
If your companion object is not named, it’s default name will be set to Companion
. Members of default named object can be accessed as a direct members of declaring class. Compiler will add Companion.*
for you automatically. Name of each object should be unique, so you can have only one default named companion with you.
When you need to access this functionality from Java you can access it in 2 possible ways:
- Invoke directly through specifying object’s name
- Marking necessary property/function with
@JvmStatic
Behind the scenes
So what happens when we need a companion object in our classes? Let’s see at the decompiled code:
Now you should have better understanding of companion object general mechanics. Each declared companion object will result in an inner static class plus it’s static instance as your class’ field. Also, take a look of what compiler add when you mark you function with @JvmStatic
:
Even though Kotlin’s approach with creating inner classes look pretty simple, it has hidden potential that can be used in various use cases.
Advanced usage
Companion objects can also implement interfaces and extend classes. This is what makes them much more interesting for me. For now I see 2 major use cases for such feature:
- Static instance of custom class across all instances of your class
- Private API implementations
Let’s look at these use cases in detail
Static instance
This use case allows you to have a single instance of specific class across all instances of your class. It helps to save memory and reduce object count (GC execution time is based on overall object count in the heap).
One of the great examples of such approach is logging. There is a great library called kotlin-logging
made by osha1 (https://medium.com/@OhadShai/logging-in-kotlin-95a4e76388f2)
Private API implementations
This use case is much more interesting. Imagine that you have some stateless code used across several classes for internal purposes. It means that you would prefer to keep this code private and don’t overload your classes’ public API. The problem of private, is that you can’t share such logic. You can declare such function as protected, but that implies that all classes using this function should be in the direct inheritance chain. You can’t achieve such behavior with interfaces since all it’s functions are public. So there’s two options left - create a utility class with needed functionality and inject it as a property in needed classes, or use companion object for such purpose. Here’s an example:
This approach allowed us share stateless logic between two unrelated classes and keep it hidden from outside invocation. Also, we didn’t declare a property for accessing this logic and made code more compact and readable. That’s what all Kotlin is about in the end.
Do keep in mind that when your companion object extends/implements something, you can still use it as a normal companion object and declare your static properties and functions.
Conclusion
I hope that after reading this small article you saw new capabilities that can help you make your code more compact and readable plus keep your classes’ public APIs clean. Or at least if you just refreshed your knowledge of such simple yet powerful construction, I will be glad. Cheers!