Given this generic interface:
interface Trick<I, T> {
fun perform(input: I): T
}
I want to get a List of all classes implementing this interface, like this:
fun <I,T>loadTricks(): List<Trick<I, T>>
I already tried to use Google's Auto-Service library but all I was able to get was a list of type List<Trick<*, *>!>
, which I think will be a problem as I desired to preserve the visibility on the actual type each implementation uses.
I've also came across the Reflections library, but that one is no longer being actively supported and, as such, I'd like to avoid it.
Maybe I am even going the wrong way about all this, so I'll just explain what I am aiming to achieve:
As you already mentioned Auto-Service and Reflections, I assume you know how to find the Class
or KClass
itself. I'll skip that part. Then the only remaining part is how to use Kotlin reflection to get I
and T
. This is more advanced usage, but it is not that hard neither:
fun main() {
val clazz = IntToStringTrick::class
val baseType = clazz.supertypes.single { it.classifier == Trick::class }
println("I: ${baseType.arguments[0]}") // Int
println("T: ${baseType.arguments[1]}") // String
}
class IntToStringTrick : Trick<Int, String> {
override fun perform(input: Int) = input.toString()
}
baseType.arguments[0]
returns a KTypeProjection
. Depending on your needs, you can acquire the variance from it. The type of I
as KClass
can be acquired with:
baseType.arguments[0].type?.classifier as? KClass<*>
You need to be careful though. If the implementation of Trick
is itself generic, then the classifier
is not a KClass
, so the above returns null. There may be other tricky cases, for example documentation mentions star projections and intersection types, but I think both of them are not possible in your case. Anyway, the above code should safely return either KClass
or null
for any such tricky cases. You could provide additional handling for these cases.