0

In Kotlin, the following seems to be reasonable code:

data class Foo(val bar: String) {
    fun combine(other: Foo): Foo {
        return Foo(bar + other.bar)
    }

    companion object Foo {
        fun someHelper() {}
    }
}

However, it does not compile: type Foo binds to Foo.Foo instead of Foo!

Is that a (language design or compiler) bug, or is this a feature? If the latter, what is the idiomatic way to implement combine in the presence of a companion object?

Of course, there is what I would consider a workaround:

fun combine(other: my.package.Foo): my.package.Foo {
    return Foo(bar + other.bar)
}

But that isn't too nice, now is it?

Raphael
  • 9,779
  • 5
  • 63
  • 94
  • Of course, I could rename the companion object. But that would defeat much of its purpose, wouldn't it? – Raphael Mar 26 '17 at 15:38
  • I don't see why it would defeat its purpose. What is your goal in naming it, and in using the same name as the class? – JB Nizet Mar 26 '17 at 15:43
  • @JBNizet If methods on companion objects are to replace static methods in any sensible way, the companion object better have the same name. Just like Scala does it, for instance. – Raphael Mar 26 '17 at 15:49
  • No, that's not how it works in Kotlin. If you want the methods in the companion object to be static methods of the class, you should annotate them with `@JvmStatic`. Otherwise, you access them, in Java, using Foo.Companion.someHelper(). In Kotlin, you can just use Foo.someHelper(). – JB Nizet Mar 26 '17 at 16:06
  • @JBNizet For the purpose of this discussion, I couldn't care less about how the Kotlin compiler represents companion objects and their methods in JVM byte code. Point is, static methods serve a certain purpose in Java, and apparently companion objects are to serve the same in Kotlin. Being of the same name as as the mother class is part of that. Considering that this exact behaviour is achieved by just leaving out the object name (see my answer below), I think my thoughts align with the designers of Kotlin. – Raphael Mar 26 '17 at 16:14
  • *Being of the same name as as the mother class is part of that*: no, it's not. Just don't give any name to your companion object, and you'll still be able to use Foo.someHelper(), as you discovered. Naming it Foo only allows using Foo.Foo.someHelper() instead of Foo.Companion.someHelper(). – JB Nizet Mar 26 '17 at 16:21
  • @JBNizet I see. I translated over how Scala's companion objects worked, not realizing that `Foo.Companion` is Kotlin's default. The shadowing makes sense if you don't treat the class name as "reserved" in some way. Thanks for explaining! – Raphael Mar 26 '17 at 21:02

1 Answers1

2

As per the documentation, the solution is to leave out the name:

companion object {
    fun someHelper() {}
}

This will create a companion object without colliding with the class name (as an explicitly named companion object Foo does). Its methods are still available as Foo.someHelper(), a shorthand for Foo.Companion.someHelper().

Raphael
  • 9,779
  • 5
  • 63
  • 94