z11i
z11i

Reputation: 1306

What difference does it make to have Companion.foo in a companion object?

In Kotlin, if I have a function in a companion object with Companion. as the prefix (Companion.foo), what difference will it make compared to a foo inside the companion object?

I noticed that in the following code, Companion.foo will make it invisible to the outside scope, but still visible to functions inside the same companion object.

You can find the code snippet at: https://pl.kotl.in/t6FvM6ni6

fun main() {
    A.foo() // compiler complains "unresolved reference"
    A.bar()
    B.foo()
}

class A {
    companion object {
        fun Companion.foo() {
            println("hello in A")
        }

        fun bar() {
            foo()
        }
    }
}

class B {
    companion object {
        fun foo() {
            println("hello in B")
        }
    }
}

Are there any other differences? Are A.Companion.foo and A.foo the same apart from the visibility? Is this a way to encapsulate methods in the companion object?


Update 1

In my actual project, I'm calling an inline function from another inline function in the companion object, which is why access modifiers can't be used. But I still would like to hide foo if possible.

class C {
    companion object {
        inline fun <reified T> Companion.foo() {
            println("hello in A")
        }

        inline fun bar() {
            foo<String>()
        }
    }
}

Upvotes: 1

Views: 269

Answers (1)

Rene
Rene

Reputation: 6268

In your example, the definition Companion.foo() is an extension as a member. In this case you define the extension in the same type A.Companion as the type of the extension. This is not useful.

The next example shows the concept of an extension as member with two different classes. The example is without companions, because it makes no difference for the concept.

class A 

class B {
    fun A.foo() {
        println("Extension for A, only visible in B")
        println("Function has two 'this' references: ${this} and ${this@B}")
    }

    fun bar() {
        val a = A()
        a.foo() // this function call is only possible in the context of `B`
    }
}

fun main() {
    val a = A()
    a.foo() // compile error
}

That all said, the two foo() function in your example have different signatures internally. The normal function foo() is a simple method on the companion object without parameters. The extension function Companion.foo() is a method on the companion object but with an extra parameter for the second this reference.

To encapsulate methods within companions, just put the private modifier before the function.

If you need an inline function, use internal and @PublishedApi to hide the function from the public API.

class C {
    companion object {
        @PublishedApi
        internal inline fun <reified T> foo() {
            println("hello in A")
        }

        inline fun bar() {
            foo<String>()
        }
    }
}

Upvotes: 2

Related Questions