Home > Back-end >  How to call Kotlin companion object function using reflection and interface
How to call Kotlin companion object function using reflection and interface

Time:10-25

Assume I have the following interface:

interface Fooable {
    fun foo()
}

The interface is implemented by named companion objects; for example:

class Impl1 {
    companion object Foo : Fooable {
        fun foo() { ... }
    }
}

class Impl2 {
    companion object Foo : Fooable {
        fun foo() { ... }
    }
}

I want to be able to map the name of each Impl class to a Fooable instance (since companion objects are always singleton instances); for example:

fun mapImplToFooable(): Map<String, Fooable> = mapOf(
    "Impl1" to Impl1.Foo,
    "Impl2" to Impl2.Foo
)

I could then call this; for example:

val map = mapImplToFooable()
map["Impl1"]!!.foo()

What I want is to be able to create the map using reflection, rather than hard coding it, assuming convention being that every Impl has a companion object Foo : Fooable { ... }

What I have so far is a function which is able to find all classes in a package, and sub-packages:

fun findAllClasses(): List<Class<*>> { ... }

And from that, I managed to get this far:

function mapImplToFooable(): Map<String, Fooable> {
    return findAllClasses()
        .filter { Fooable::class.java.isAssignableFrom(it) }
        .map { clazz -> it.name to clazz } // This is a problem...
        .toMap()

The problem is that clazz is a Class<Fooable> rather than an instance of Fooable (in each case, the companion object).

How do I obtain the companion object instance, rather than just a Class<Fooable>?

CodePudding user response:

Use the Kotlin reflection API (rather than the Java one), so that you have access to objectInstance:

fun mapImplToFooable(): Map<String, Fooable> =
    findAllClasses()
        .filter { 
            Fooable::class.java.isAssignableFrom(it) &&
            it != Fooable::class.java // you should also check this if findAllClasses can return Fooable
         }
        .map { 
            // you should get the declaring class's name here
            clazz -> clazz.declaringClass.name to clazz.kotlin.objectInstance as Fooable }
        .toMap()
  • Related