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

I have a Dao interface which is populated by useful extension methods such as findAll(), getById(), deleteAll() etc. With SQL or JPA, you only need class name to be able to construct the SQL query (delete all from Person etc), so this works nicely. To be able to have Person.deleteAll(), all you need in Kotlin is to have the companion object implement the Dao interface; the extension methods will then be attached automatically.

You can see this code in practice here: vaadin-on-kotlin/CrudView.kt at d3e740c1644168357486519e6fabc1cbed662477 · mvysny/vaadin-on-kotlin · GitHub
Highly addictive code style :slight_smile: All you need to do is to type Person. and IDE will auto-complete all dao methods. Of course you can introduce a convention in your project, say, PersonService which will do the same, but with Kotlin you don’t have to - the convention is built into the language itself.

3 Likes

See Question about static methods and inheritance where Andrzej Breslav is saying: “The Java Puzzlers book shows how bad it is when static methods are inherited. It is a consious decision not to have it in the language.”

3 Likes

A drawback: You can’t import “on demand” (import package.Class.Companion.*). That’s annoying if you have many vals and want to import them all, for example, to use in tests (my case).

The best workaround would be to put them in another package as top level, but it’s not the same – you won’t be able to use private members to define them, unless you declare each val in the companion and copy there (DRY!).

It would be nice to have at least some (static…) local scope (C++ namespace-like) to use anywhere so you could import *. Or does something like that exist and I just don’t know?

There are two things that I don’t like about Kotlin: primary constructors and companion objects. While some arguments can be made for primary constructors, companion objects are definitly more hindrance than help. I vote for a static keyword.

1 Like

Based on the coding style laid out you should use top-level function or a extension function. I see a lot of java does this and that which seems like a bad paradigm to reference.

Python, swift, and javascript use top-level function the exact way that has been described here. They use module or pacakge-level functions. Stop scoping you’re functions to classes.

Everything is package-level; it’s impossible to not belong to a package. The difference is one thing is a Global GC root with a permanent lifespan and the other is a leaf that can be reclaimed.

In Kotlin you can just put things in a file that makes sense for it to be in a file. As many helper methods and configuration variables as you want. There’s no need to adhere to Java’s one-file = one class pattern. Because in the end all it did was make people couple together things that don’t share the same life-cycle because they wanted them in the same place.

But there not global-level though, they are package level. I would put them together because the function has a explicit association with that class.

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

That’s not true. Package level functions are available to Java Code. Every file that contains package-level declarations will produce a class, under which the declarations are accessible as static members, so, it’s exactly what you need. By default, a file called Strings.kt will produce a file-class called StringsKt, but you can even rename that for more intuitive calling from java code, by putting a @file:JvmName("StringUtils") annotation at the very top of the file. From the Java POV, this will produce a class called StringUtils, which has all the package-level functions of the file accessible as static methods, it’s exactly what you want when calling from Java.

I’m all for great interoperability, but I think the omission of statics from the language was a good design choice and not worth compromising when there are perfectly fine alternatives if you need those statics to call from Java.

PS.:
Quick word about the inline and @InlineOnly thing: @InlineOnly is an internal annotation marking functions that should never be called directly without inlining. This might seem to be the same as regular inline at first, but regular inlined functions can still be called from Java Code, where they obviously can’t be inlined so they are called as regular functions. @InlineOnly tells the compiler “This is a function that can only be called from Kotlin, don’t make it available as a regular callable function to other bytecode.” Code outside the internal Kotlin libraries can’t use that annotation, it’s basically just to hide all those Kotlin utilities from Java code.

4 Likes

Seems kotlin is leaking quite a bit of implementation detail here; I would expect the compiler and runtime to be smarter here instead of needing a lot of handholding. Inlining is certainly not something people worry about when writing Java. Just happens. That was one of the earliest features when jit compilation was introduced way back with 1.1

Regarding static vs. companion vs. package level functions, this clearly needs better documentation as the documentation seems to be pushing people towards companion objects. The more I read about this the more I’m convinced the decision process here was flawed. I’d recommend just respecting 20 years of Java historical use of static and just adding it to the language instead of requiring hoops to be jumped through and generating broken companion objects in the automated conversions. Compiler & jvm supports it and the language is already full of other syntactic sugar. Which makes this a strange omission. And given the amount of people genuinely confused about the workarounds here and the added verbosity, the lack of static is causing a lot of unnecessary friction.

1 Like

I agree that the compiler could be smarter regarding inlining, but I assume it would be a lot of work to develop those compiler smarts to actually make this work. Maybe the Kotlin team is already working on that. I definitely think it’s better to have some manual effort than a compiler that is hit-or-miss when inlining functions. You might not care if it was only performance, but inlining actually gives you additional features like reified type parameters and non-local returns from lambdas, so a compiler meddling with that would have to be rock-solid in knowing what needs to be done.

Also agreed on the need for better documentation regarding how to handle what would be statics in Java. The automatic conversion can’t simply be switched to package level functions, though, because that would change semantics for the calling Java code because of the now missing class name. That’s a choice that the developer has to make, and the default option should be the one that’s closer to Java, even if it’s not the optimal solution at times. Also note that classes that consist entirely of static members already get converted to a standalone object without an enclosing class, bypassing the problem entirely.

And no, Java doing something for 20 years is not a valid argument for anything, or else, we wouldn’t have needed Kotlin at all.

1 Like

And if you don’t want to you don’t have to worry about it. Kotlin functions get inlined by the JIT compiler the same way Java functions are. That being said there are a few advantages of sometimes inlining code at compile time therefor having the keyword is definitely useful.

I think the documentation of how Kotlin code is represented in Java is really good, but maybe you can point out where it is unclear.
Also I don’t see how it pushes people towards using companion object as it states here

In Kotlin, unlike Java or C#, classes do not have static methods. In most cases, it’s recommended to simply use package-level functions instead.

I don’t think a static keyword would fit in Kotlin. Kotlin itself is build to work without statics. I can’t talk about the conversion tool from java to kotlin as I never used it, so maybe it needs to be improved but this is no reason to change the language.
Also adding a keyword just so java interop works better feels wrong, we already have the @JvmStatic annotation for that.

3 Likes

Blockquote I think the documentation 3 of how Kotlin code is represented in Java is really good, but maybe you can point out where it is unclear.

I think the many threads on stackoverflow on this topic, this lengthy thread, and similar criticisms I’ve read on Kotlin from java developers suggest that there is a problem here with people getting confused and that people are genuinely wondering what the big deal is around not simply supporting static. Just saying, this is a topic that comes up regularly and the tone in this thread strikes me as a bit defensive.

Blockquote I don’t think a static keyword would fit in Kotlin. Kotlin itself is build to work without statics. I can’t talk about the conversion tool from java to kotlin as I never used it, so maybe it needs to be improved but this is no reason to change the language.

I believe the opposite is the case and your argument seems to boil down to language esthetics. Kotlin’s compiler supports static just fine, so does the JVM. Kotlin is all about providing syntactic sugar and less verbose ways of doing things and making the transition from Java seamless. Static methods seem to be a strange exception. The implementation of static seems trivial, so that’s not the issue. It’s clearly more concise, easier to explain to java developers, and there seem to be lots of other keywords including “inline” that you could equally argue should not be language keywords that made it into the language.

The conversion tool does the wrong thing with static methods unless it is acceptable to you to have it refactor all your Java code to make it aware of the .Companion inner class (speaking of ugly). It seems to not be aware of the annotation that avoids this, so this might be fixable. IMHO having kotlin’s .Companion exposed to java code is not really acceptable in any published Java API (speaking of ugly).

1 Like

I would not say that this is a 100% fitting description of Kotlin. Kotlin takes a stance on many design principles. Static members are a try of adding functions that are not part of any object.
I personally have seen only 2 things that static functions are used for.

  1. Instance creation (singletons, etc)
  2. Utility functions (eg. java.lang.Math)

Singletons have their own special syntax in Kotlin (object keyword) so we no longer need static for those. Instance creation is a good use for the companion object and utility functions like in the case of java.lang.Math are definitely better done using global functions in Kotlin.

As for the need of Java interop, I don’t see why this requires a keyword when it can be done using an annotation.

Yes Kotlin decided to take a different approach to this and switching from Java you have to learn this new approach. This alone is no reason to add static though IMO.
Also the problem is not the documentation but that people don’t want to change their way of programming, is that what you are saying?

I would not call the tone in this topic defensive. I just genuinely don’t understand which usage of static is missing in Kotlin. As far as I am aware everything that static is used for can be done in Kotlin (although be it in a slightly different way).

Agreed static seems trivial for people how have used it for years and years, this does not make it a necessary feature.
The thing is we are arguing about the possible gains of a feature without any specific context. It’s obvious that we have different positions about this and I don’t think this will change without any concrete examples. I would love to see a code example where you would say static is better than anything Kotlin can offer so far.

About inline: inline adds a lot of amazing features to kotlin which would otherwise not be possible (reified and returns from inline for example). Also the argument: “Kotlin has unnecessary keywords already therefor we should add one more” does not really make any sense :wink:

If you care about static just for the Java interop, put your static methods into the companion object and annotate them with @JvmStatic. That way you can use them as static from Java and Kotlin alike as any method of the companion object can be accessed using the name of the class.

1 Like

I’m the person who initiated this thread so I’ll put in my 2 cents once again.

For my programs, it’s not about Java interop. Not one bit.

The static keyword for methods and class variables in Java is simple and very useful for storing and retrieving app global, package global or class global state information, while hiding it from contexts that don’t need to know about it. Depending on the runtime context (a virtual machine, a mobile app, a standalone headless program, etc.), this concept (a global within a compiler-level context without having to use an instance variable as an anchor) is very useful. IMO, the designers of Kotlin are dogmatic about their FP principles, and by omitting a very simple and useful non-FP construct (static), they were signaling their allegiance to the FP religion. Yep. I don’t use that word lightly. Whether we’re talking about actual religion, or Trump vs. Hillary, or FP vs. OO programming, it is a faith in the rightness of one’s cause. Note I said faith. Not reason or pragmatism. Faith.

A Kotlin companion object is a very ugly solution for the need to have ‘global’ state within a context larger than an instance.

FWIW, we have decided against moving our code base (a million or so hand-written lines and even more machine generated) to Kotlin. The biggest reasons were:

  1. No support for Eclipse or other LSP-based development environments
  2. The language designers eschewing time-tested OO design principles in favor of FP constructs.
  3. The hook up with Google (that’s another story that’s not technical so I won’t go further).

Static is not even an OOP construct! The opposite is true: companion objects are much more object oriented than static members. Have you ever thought about how strange static members are together with inheritance?

2 Likes

Static is not even an OOP construct! … Have you ever thought about how strange static members are together with inheritance?

I disagree. There are 4 aspects of OO programs: classes and instances; encapsulation; inheritance; polymorphism. Certain constructs of OO languages (e.g. Java) implement those aspects. The Java static construct implements the concept of encapsulation quite well. Therefore static, as implemented in Java, is an OO construct. Kotlin’s top level function construct doesn’t qualify as it can’t be hidden, therefore it doesn’t qualify as encapsulation.

Your not supposed to use companion objects as a replacement for statics. You use package-level functions and extension functions. The only time you should ever use a companion object is for creating factory methods.

What do you mean by “It can’t be hidden?” Top level functions can be made private or internal just as static methods of classes can.

Also, I completely agree with Wasabi here, especially that it would be interesting to see a specific, contextual example of where a static method does the job better than anything Kotlin can offer, because right now, I can’t think of one either.