Pass type from variable - kotlin

I want to use method from the external library to load my assets files, declaration of that function:
inline fun <reified T> loadSync(path: String): T = loadSync(getAssetDescriptor(path))
And I would like to have enum to store all my assets paths and types, currently, it looks like that:
enum class Asset (val path: String, val clazz: Class<*>){
// Textures
LOADER("splash-screen/loader.png", Texture::class.java),
// ...
}
and it will be great if it will be possible to make something like that
assetStorage.loadSync<Asset.LOADER.clazz>(Asset.LOADER.path)
but I don't know how to correct declare clazz

You cannot use an expression like Asset.LOADER.clazz for a Generic type argument. You would either have to provide the type as
assetStorage.loadSync<Texture>(Asset.LOADER.path)
or have to create a wrapping function around the loadSync function passing the class as an argument, so that the compiler can infer the type as below
#Suppress("UNUSED_PARAMETER")
inline fun <reified T> newLoadSync(path: String, clazz: T): String = loadSync<T>(path)
and use it as
assetStorage.newLoadSync(Asset.LOADER.path, Asset.LOADER.clazz)

Related

Using KClass as argument to templates

I used the following link to get around the limitations arising from type erasure in Java.
kotlin reified generic in virtual function
I want the following class to be interfaced as follows:
class KlaxonJsonParserAdapter(private val klaxon: Klaxon) : JsonParser {
override fun <T: Any> parse(string: String, type: KClass<T>): T = klaxon.parse<type>(string)
}
Subsequently, it enables to use it as such:
JsonParser.parse<Type>(string: String)
through extension functions.
But I couldn't figure out how to use the type parameter to external dependency as a reified type to achieve what I want.
I cannot plug the variable into the template slot as such:
klaxon.parse<type>(string)

Overload resolution of reified generic in Kotlin

I would like to return different type depending on a reified type parameter. I tried to use overloads but the overload resolution doesn't seem correct.
My goal is to store a close set of types at runtime like so:
sealed interface Type<T> {
object Int: Type<kotlin.Int>
object Boolean: Type<kotlin.Boolean>
}
inline fun<reified T> get() : Type<T> = getImpl(null as T?)
fun getImpl(a: Int?) : Type<Int> = Type.Int
fun getImpl(a: Boolean?) : Type<Boolean> = Type.Boolean
fun <T>getImpl(a: T?) : Type<T> = error("Unsupported type")
fun main() {
println(getImpl(null as Int?)) // return Type.Int as expected
println(get<Int>()) // Same as above after get is inlined but throws!
}
Could it be that the overload is resolved before the method is inlined?
The goal is for some generic classes to take a Type<T> parameter and be guaranty that T is in the closed set. It also allows for testing the generic type T at runtime (workaround type erasure).
I would rather avoid having the clients specify Type.Int explicitly or have an implementation using unchecked cast such as:
inline fun<reified T> getUncheckedCast() : Type<T> =
when (T::class) {
Int::class -> Type.IntType as Type<T>
Boolean::class -> Type.BooleanType as Type<T>
else -> error("Unsupported type")
}
I think your last code block is the best solution. Although your get function is reified, the type is still generic so the compiler is going to resolve the overload as the generic one that throws an error. You can’t get the compiler to select which overload to call at runtime. It is always selected at compile time.
According to Kotlin documentation, the only difference reified parameter makes is that its runtime class is available:
4.5.2 Reified type parametersLoad tests
Type parameters of inline function declarations (and only those) can be declared reified using the corresponding keyword. A reified type parameter is a runtime-available type inside the function scope, see the corresponding section for details.
It doesn't specify that the type is substituted when the function is inlined.
This mean that reified is sugaring for implicitly passing the KClass of reified types:
inline fun <reified T>f() = T.class
// is desugared to
inline fun <T>f(__TKClass : KClass<T>) = __TKClass
Thus the overload resolution set is not affected by reifying types.

Why should I implement a function type as an interface in Kotlin

I came across something and wondered all the time why you should do this.
You implement an interface in Kotlin through a simple function type:
"It is possible for a class to implement a function type as if it were an interface. It must then supply an operator function called invoke with the given signature, and instances of that class may then be assigned to a variable of that function type:"
class Divider : (Int, Int) -> Double {
override fun invoke(numerator: Int, denominator: Int): Double = ...
}
But why should I do this? Why should I add an interface in that way? I think its only possible to add one function and not more.
Or is it an advantage that I can implement a function with a function body and not only the function head like in normal interfaces? I think it is possible in Java to add default methods to interfaces with a function body. So maybe it is something like that?
Function as a class can have state. For example you could store the last invocations and use the history as a cache:
class Divider : (Int, Int) -> Double {
val history = mutableMapOf<Pair<Int, Int>, Double>()
override fun invoke(numerator: Int, denominator: Int): Double {
return history.computeIfAbsent(Pair(numerator, denominator)) {
numerator.toDouble() / denominator.toDouble()
}
}
}
fun main() {
val divider = Divider()
println(divider(1,2))
println(divider(2,3))
println(divider.history)
}
It is probably not very useful to write a class that only implements a function type interface; however, it might be useful to write a class that can among other things be used in place of a function.
An example from the standard library is the KProperty1 interface. You can write code like this:
data class C(val id: Int, val name: String)
val objs = listOf(C(1, "name1"), C(2, "name2"), C(3, "name3"))
val ids = objs.map(C::id)
Here, C::id is a property reference of type KProperty1<C, Int>, and it can be used as an argument to List.map in place of a lambda because KProperty1<C, Int> extends (C) -> Int. However, KProperty1 has a lot of other uses besides being passed as a function.

Type- or Class-keyed map

In Kotlin I can use filterIsInstance to obtain a type-specific (and type-safe) sub-collection:
val misc: List<Any> = listOf(42, 3.14, true, "foo", "bar")
val strings: List<String> = misc.filterIsInstance<String>()
println(strings) // => [foo, bar]
But I have a large collection of objects and I would like to pre-sort it into a Map, by concrete type. Is it even possible to define such a map in Kotlin's type system?
val miscByType: Map<KType, Collection<???>>
or:
val miscByClass: Map<KClass, Collection<???>>
Should I use a custom implementation with unsafe (but logically sound) casts?
The following is such an implementation. It works, but I'm wondering if there is a less hacky way of doing it:
import kotlin.reflect.KClass
class InstanceMap {
// INVARIANT: map from KClass to a set of objects of *that concrete class*
private val map: MutableMap<KClass<*>, MutableSet<Any>> = mutableMapOf()
// this is the only public mutator, it guarantees the invariant
fun add(item: Any): Boolean =
map.getOrPut(item::class) { mutableSetOf() }.add(item)
// public non-inline accessor, only needed by the inline accessor
fun get(cls: KClass<*>): Set<*>? = map[cls]
// inline accessor that performs an unsafe, but sound, cast
#Suppress("UNCHECKED_CAST")
inline fun <reified T> get(): Set<T> = get(T::class) as Set<T>? ?: setOf()
}
fun instanceMapOf(vararg items: Any): InstanceMap = InstanceMap().apply {
items.forEach { add(it) }
}
val misc = instanceMapOf(42, 3.14, true, "foo", "bar")
val strings = misc.get<String>()
println(strings) // => [foo, bar]
Your code looks OK. The only problem is with the unchecked cast warning. At the JVM bytecode level, the cast does nothing, because of the way, how generics are implemented in Java and Kotlin. It is also known as type erasure.
https://en.wikipedia.org/wiki/Type_erasure
Type erasure adds yet another problem to your code - it does not tell generic type arguments. So that, for example, List<Int> has the same class as List<String> or List<Map<String, Object>>
Do you expect your code to find superclasses or interfaces in the map? E.g. if I have
interface A
interface B
class C : A, B
val m = InstanceMap()
m.add(C())
m.get(C::class)
m.get(A::class)
m.get(B::class)
Do you expect these 3 calls to return the same value?
The JVM standard workaround for it is to explicitly pass Class<T> parameter and use the Class#cast method to cast instead. That change will make the code safer.
There is a remedy to type erasure in Kotlin. You may add a reified inline function so that Kotlin compiler will use the exact type in the inlined generic function body
https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters
inline fun <reified T> InstanceMap.get() = get(T::class)

How to get generic param class in Kotlin?

I need to be able to tell the generic type of kotlin collection at runtime. How can I do it?
val list1 = listOf("my", "list")
val list2 = listOf(1, 2, 3)
val list3 = listOf<Double>()
/* ... */
when(list.genericType()) {
is String -> handleString(list)
is Int -> handleInt(list)
is Double -> handleDouble(list)
}
Kotlin generics share Java's characteristic of being erased at compile time, so, at run time, those lists no longer carry the necessary information to do what you're asking. The exception to this is if you write an inline function, using reified types. For example this would work:
inline fun <reified T> handleList(l: List<T>) {
when (T::class) {
Int::class -> handleInt(l)
Double::class -> handleDouble(l)
String::class -> handleString(l)
}
}
fun main() {
handleList(mutableListOf(1,2,3))
}
Inline functions get expanded at every call site, though, and mess with your stack traces, so you should use them sparingly.
Depending on what you're trying to achieve, though, there's some alternatives. You can achieve something similar at the element level with sealed classes:
sealed class ElementType {
class DoubleElement(val x: Double) : ElementType()
class StringElement(val s: String) : ElementType()
class IntElement(val i: Int) : ElementType()
}
fun handleList(l: List<ElementType>) {
l.forEach {
when (it) {
is ElementType.DoubleElement -> handleDouble(it.x)
is ElementType.StringElement -> handleString(it.s)
is ElementType.IntElement -> handleInt(it.i)
}
}
}
You can use inline functions with reified type parameters to do that:
inline fun <reified T : Any> classOfList(list: List<T>) = T::class
(runnable demo, including how to check the type in a when statement)
This solution is limited to the cases where the actual type argument for T is known at compile time, because inline functions are transformed at compile time, and the compiler substitutes their reified type parameters with the real type at each call site.
On JVM, the type arguments of generic classes are erased at runtime, and there is basically no way to retrieve them from an arbitrary List<T> (e.g. a list passed into a non-inline function as List<T> -- T is not known at compile-time for each call and is erased at runtime)
If you need more control over the reified type parameter inside the function, you might find this Q&A useful.