I have a use case where I need to create a map mapping from KClass to a (lambda) function that converts instances of that class to something else (in my case a String). In java, I would write something along the lines of this:
private Map<Class<?>, Function<?, String>> mappers = new HashMap<>();
In Kotlin, the syntax for a lambda parameter should be (Something...) -> ReturnType
, but this does not accept either *
, nor in
or out
. Note, that the map should be able to contain any mapper taking any argument that is of the specified type or more specific in order to be most lenient (however since this is dynamic, it will not be validated. It is just important to know for casting purposes).
How is this expressed correctly?
How I solved it in the end
Thanks to @broot and @Sweeper, I was able to implement my use case. Because I had some other issues, and I assume that anyone who finds this has a similar use case, I want to add the rest of the code in question. I ultimately went with the following (reduced to it's essentials):
// note that this map is public because it is accessed by a public
// inline function. Making it private won't compile.
val mappers = HashMap<KClass<*>, (Any) -> String>()
// this is used to add mappers to the map of mappers.
// note the noinline!
inline fun <reified T> mapping (noinline mapper: (T) -> String) {
mappers[T::class] = mapper as (Any) -> String
}
// This is the only method accessing the map to extract mappers and
// directly use them.
fun mapToString(obj: Any): String {
// some stuff here...
// Attempt to map to the id via a predefined mapper.
candidate = mappers[obj::class]?.let { it.invoke(obj) }
if (candidate != null) return candidate
// some other fallback here...
}
Also note that all of the above is nested within another class which I will for the sake of argument call Cache
. This is the unit test:
@Test
fun `test simple extractor`() {
class SomeClass(val somethingToExtract: String)
val someInstance = SomeClass("someValue")
val cache = Cache()
// defining the extractor
cache.mapping<SomeClass> { it.somethingToExtract }
// using the extractor
val id = cache.mapToString(someInstance)
assertEquals(id, someInstance.somethingToExtract)
}