Accessing companion members from instance?


#1

Say I have a Foo class with bar() method, and several classes, A, B, C, whose companion inherits from Foo. Now, I have a variable, x, and I know it is of type A, B or C (but not exactly which one). I would like to call bar() from x’s companion. How would I do this?

I tried x.bar() (which would be ideal for me) and x.Companion.bar() and neither compile.What’s the idiomatic way to do this?

Note: I’m aware I could do a switch so if x is A then A.bar(), etc, but that’s highly unpractical.


#2

Give A,B,C a common super interface Fooable that has a property val foo of type Foo. Let them return their own companions in this property. Then use fooable.foo.bar(). More specifically, your variable x needs to be of type Fooable. That way you can retrieve it’s foo property.


#3

So, there’s no general way to go from an object to it’s class companion out of the box? An implementation has to bre provided in each class to do that?

Also, any idea why is this a problem?

 class Myclass {
   companion object {}
   val comp = Companion //error, says I need getter. Why?
   val comp2 
     get() = Companion //this is fine though
 }

#4

That’s not the main problem that you are facing. The main problem is that your variable x needs to be of some type that you can work with. If its type is Any you will not be able to achieve anything meaningful with it.

Even if there were a way to retrieve the companion of Any variables, what type would that companion reference be? It would need to be of type Any (more specifically Any?). This means you wouldn’t be able to invoke your custom member functions on that companion reference.

The solution is to have a variable x of type Fooable, which allows us to access the foo property.

You are probably coming from a dynamicly typed language, aren’t you? Javascript maybe?

Also, any idea why is this a problem?

I can only make assumptions here. Nothing that I am confident enough to share.


#5

The problem is not with coming from dynamic languages at all… My train of thought was more like: “I know at compile time that a given variable’s type has a companion with a given method, so the compiler should be able to ‘go’ from the variable to the method wothout me explicitly telling it that it can go there”.

Anyway, I guess that if I have a member val pointing to the companion, I can do something like class MyClass: IAccompanied by companion.


#6

I consider this as a root problem

Kotlin not


#7

You can use Kotlin reflection to accomplish what you want, but you’ll need kotlin-reflect:

import kotlin.reflect.full.companionObjectInstance

abstract class Foo {
    abstract fun bar(): String
}

class A {
    companion object : Foo() {
        override fun bar() = "I'm the companion of A"
    }
}

class B {
    companion object : Foo() {
        override fun bar() = "I'm the companion of B"
    }
}

class C {
    companion object : Foo() {
        override fun bar() = "I'm the companion of C"
    }
}

fun main() {
    val x: Any = listOf(A(), B(), C()).random() // An instance of A, B or C
    val whoAreYou = (x::class.companionObjectInstance as Foo).bar()
    println(whoAreYou)
}

In this code you are sure x is one of A, B or C so theoretically there should be no problem when casting to Foo, but in case you cannot ensure that just replace the cast with a safe cast:

val whoAreYou = (x::class.companionObjectInstance as? Foo)?.bar()

#8

He asked for an idiomatic way. Using reflection allows for code that is similar to how it would work in a dynamicly typed language. It is for sure not an idiomatic Kotlin solution.


#9

Oh, I didn’t see that :sweat_smile: but you are right.
However, I just wanted to show that what he asks is possible, even though it uses reflection.


#10

Fair enough :wink:

If the target platform is Javascript it would also be possible without reflection, by using Kotlin’s dynamic type. However, this also is not a nice idiomatic solution.