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()