What is the advantage of "companion object" vs static keyword

I’ve been transitioning some java code and the lack of automated conversion of static methods makes this much more painful than it should be; it breaks the automated conversions in intellij. You end up with broken imports to Companion, missing @JvmStatic annotations, and other crap like that, etc. Lots of manual work to fix what should be easy. Also it makes exposing kotlin code to Java a bit more tedious than necessary.

Static in java makes sense whenever you have something that does not require object state (i.e. a function). Which is why e.g. java.util.Math only has static functions. That usecase also exists in Kotlin. Looking at kotlin.math, I see that functions are defined outside a class and not in companion block. They also have both inline and an @InlineOnly annotation (doesn’t one imply the other?), this seems to me like it is both less clean and leaking compiler optimization related things like inlining where Java implies the same with a static keyword and the jvm inlines when needed without needing to be told.

Sadly, you can’t use these functions from Java because they are not in a class. You could argue that sticking static methods in a class that you are not going to create objects of is language weirdness in Java that results from the rule that methods have to be in classes.

So, there are two ways to emulate static methods in kotlin and they both kind of suck from an interoperability point of view. Package level functions are not usable from java but clearly better from a language design point of view and companion functions need an extra annotation if you don’t want to import Companion all over the place on the Java side. The former is what you probably should be using if you want the equivalent of static functions in kotlin except if you are planning to expose them to Java in which case you want a companion object with the @JvmStatic annotation.

In short, I think Kotlin is suffering from leaky abstractions (compiler optimizations, java interop) a bit here with the overuse of annotations and could benefit from better interoperability with Java as well. Maybe just adding static class methods to the language removes the need for a lot of verbosity and buys more straightforward interoperability at the same time.

A clean solution could be to simply allow the user to define companion fun foo() instead of companion { fun foo() …}. I’m sure this has been discussed, but would respectively like to point out that this thread is long, stackoverflow is full of lengthy explanations of companion, and any java developer is running into this … There’s a problem lurking here. Why not solve it?

2 Likes