Companion Functions

When writing Kotlin code it becomes a pain to constantly create entire code blocks just for a single function; ex.

companion object {
    
    fun something() : Int {
        return 1 + 1;
    }

}

Could a more concise syntax for this be allocated? ex.

companion fun something() : Int {
    return 1 + 1;
}

This would really help clean up a lot of classes

5 Likes

It would be nice if they allowed us to use the JVM static annotation on non-companion functions.

1 Like

I’m not sure that the case of having only a single function or field in a companion object is super common. Even if it were, this is really just the static keyword isn’t it? The arguments against that are far more established.

1 Like

Neither is delegation, but we have syntax sugar for delegating to a type using the “by” keyword.

Not at all, I am not proposing that this syntax be allowed for multiple fields, but simply for when you only have a single property/function within the companion object. It would allow for its declaration to be much more concise.

1 Like

A lot of language design is about return on investment. Delegation is an awesome pattern that benefits a lot from language-level support. Being able to express a very specific situation in 8 fewer characters? Maybe not. It’s the difference between support for an entire programming pattern and support for very specific “boilerplate” reduction.

I think the extra limitation of only being able to have one actually hurts the proposal, if anything.
Say I have a “companion fun” or property and I want to add another. Now, I have add the two to a new companion object and remove the keyword from the original function. While not a huge deal, it’s still extra work in refactoring that didn’t exist previously.
In my opinion, this would also be counter-intuitive to people coming to Kotlin from Java. This almost looks like some “static” keyword, but then it’s not.

2 Likes

I don’t really consider this to be simply for reducing the amount of characters you type. The point is to reduce the amount of ceremony involved in doing something like this, which IMO is one of the core aspects of Kotlin’s design.

I can understand how this might be an issue if you are programming in notepad, but when using IntelliJ, this is not an issue. It has many intents that can convert code to be almost anything, even between languages. If it detects that you try to do this, it can easily move them both into a single companion object.

I agree, but honestly, the entire language is almost counter-intuitive considering its relatively odd syntax for doing some things. This is an issue that persists beyond just this one thing. You wont truly know the language, until you learn the language. This seems like a moot point. IMO

May I ask you for an example? What do those functions do? Why are they not members of the class, or members of another object, or top-level functions?

Yes, in a current project, I have a constant that represents time to wait before cancelling a specific task. To do this I must do:

companion object {                  
                                
    private const val DROP_TIME = 40
                                
}                 

This would be much nicer if for one, constants could be in other places besides objects and top level, or this constant could be declared on a single line. Perhaps:

private companion const DROP_TIME = 40

And yes, I omitted the “val” on purpose, because its existence in constant declaration is odd to me. "const"s can never be var, so why explicitly have to declare it as val. Just seems like more useless ceremony.

I know, it’s not a very pretty solution, but it’s what I’d prefer over entire code blocks for a single property/function.

Why not simply put it as a top-level variable in the file? Does it really have to be part of the class?

1 Like

If you want it to be ‘private’ to the class, then this would rule out using a top-level declaration.

I agree with the OP that it would be nice to have a shorter syntax for dealing with single non-instance class members though I wouldn’t be surprised to see ‘true’ static members introduced to the language (either in addition to or instead of companion objects) which would solve this and other problems.

With regard to the ‘const’ modifier, I think the reason why you need to specify ‘val’ as well is so that ‘const’ doesn’t have to be a keyword i.e. it can be used in other contexts without (too much) confusion.

Well, you can define something like

private const val SOMETHING = "something"

public class Test {
    fun someFun() {
        println("$SOMETHING")
        val test = SOMETHING + "value"
    }
}

And it compiles just fine. Because it’s a const, it gets inlined. BUT a new class TestKt is created that contains a private static final SOMETHING in it, so a bit of waste.

Removing the const creates a public get in the TestKt class, so you might be able to access it in Java, but can’t in Kotlin.

If you use a companion object, the companion gets created as an inner static class.

So you can create it as a top-level const, saving the typing of ‘companion object’
 Not sure what the recommended/better approach is.

If the const can’t be inlined, a public access is created, but it’s a non-obvious name.

I think it’s a case of style choice. Companion keeps it more encapsulated if you’re using Java too. If you’re 100% Kotlin, then you can safely save the extra typing by putting the const at the top-leve.

This style does not conform to what I proposed, this top level declaration will not be accessible through the class like companion members can.

Functionally it is the same. It is scoped to the file (or artificial class created for the file), so yes, if you created another class in the same file, it is not scoped ONLY to the one class.

You marked the function/const as private, so I’m not sure what you mean as ‘accessible through the class’. It is accessible in the class, and has limited scope because it’s private.

As it’s private, it’s not accessible anywhere else anyway.

What am I misunderstanding here?

1 Like

The most recent post of mine was only an alternate example of where it could be applicable, the focus of this thread is the example in the OP. That’s what you’re misunderstanding.