Using KClass as argument to templates - kotlin-reified-type-parameters

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)

Related

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.

How to make a generic function for enumValues<T> in Kotlin?

I struggle with providing a type as parameter for a procedure that uses the enumValues<MyEnum>() function.
Got it to work with reified but using inline functions all the way is no option for me.
fun <T: Enum<Trait>> traits(
selectionState: SnapshotStateMap<Trait, Boolean>
) {
val chunks = enumValues<T>().toList().chunked(5)
chunks.forEach {
Row {
it.forEach {
TraitIcon(it, selectionState)
}
}
}
}
My enums all derive from enum class Trait. So in fact I want to pass enum class TraitFoo: Trait, enum class TraitBar: Trait and so on into the function.
Cannot use 'T' as reified type parameter. Use a class instead.
Is the compile error I receive here. Any idea of solving this? I am somewhat confused why this is not working.
Looking at the implementation of enumValues:
public inline fun <reified T : Enum<T>> enumValues(): Array<T>
I see it uses reified. That does mean the type has to be known at compile time. Therefore I can not pass a generic but need to pass an explicit type? Is that the issue?
If yes - is there another way to achieve this rather than using reified ?
If you want to be able to use T in your function as if it's a real type then it must be reified. And in order for a type parameter to be reified it must be part of an inline function. So you're going to need an inline function.
The next bit is figuring out the generics. You currently have:
<T : Enum<Trait>>
That means, due to the nature of enums, that T can't possibly be anything other than Trait. However, you have since clarified that Trait is not an enum but is actually an interface that's implemented by various enum classes. So what you really want is T to be bounded by both Enum<T> and Trait.
Given all this, I believe what you're looking for is the following:
inline fun <reified T> traits(
selectionState: SnapshotTraitMap<Trait, Boolean>
) where T : Enum<T>, T : Trait {
val chunks = enumValues<T>().toList().chunked(5)
chunks.forEach {
Row {
it.forEach {
TraitIcon(it, selectionState)
}
}
}
}

Pass type from variable

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)

why there is 'by' for the extended class and reified in function define

coming across a sample with a class and a function and trying to understand the koltin syntax there,
what does this IMeta by dataItem do? looked at https://kotlinlang.org/docs/reference/classes.html#classes and dont see how to use by in the derived class
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
class DerivedStreamItem(private val dataItem: IMeta, private val dataType: String?) :
IMeta by dataItem {
override fun getType(): String = dataType ?: dataItem.getType()
fun getData(): DerivedData? = getDataItem()
private inline fun <reified T> getDataItem(): T? = if (dataItem is T) dataItem else null
}
for the reference, copied the related defines here:
interface IMeta {
fun getType() : String
fun getUUIDId() : String
fun getDataId(): String?
}
class DerivedData : IMeta {
override fun getType(): String {
return "" // stub
}
override fun getUUIDId(): String {
return "" // stub
}
override fun getDataId(): String? {
return "" // stub
}
}
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
There is some good documentation on reified type parameters, but I'll try to boil it down a bit.
The reified keyword in Kotlin is used to get around the fact that the JVM uses type erasure for generic. That means at runtime whenever you refer to a generic type, the JVM has no idea what the actual type is. It is a compile-time thing only. So that T in your example... the JVM has no idea what it means (without reification, which I'll explain).
You'll notice in your example that you are also using the inline keyword. That tells Kotlin that rather than call a function when you reference it, to just insert the body of the function inline. This can be more efficient in certain situations. So, if Kotlin is already going to be copying the body of our function at compile time, why not just copy the class that T represents as well? This is where reified is used. This tells Kotlin to refer to the actual concrete type of T, and only works with inline functions.
If you were to remove the reified keyword from your example, you would get an error: "Cannot check for instance of erased type: T". By reifying this, Kotlin knows what actual type T is, letting us do this comparison (and the resulting smart cast) safely.
(Since you are asking two questions, I'm going to answer them separately)
The by keyword in Kolin is used for delegation. There are two kinds of delegation:
1) Implementation by Delegation (sometimes called Class Delegation)
This allows you to implement an interface and delegate calls to that interface to a concrete object. This is helpful if you want to extend an interface but not implement every single part of it. For example, we can extend List by delegating to it, and allowing our caller to give us an implementation of List
class ExtendedList(someList: List) : List by someList {
// Override anything from List that you need
// All other calls that would resolve to the List interface are
// delegated to someList
}
2) Property Delegation
This allows you to do similar work, but with properties. My favorite example is lazy, which lets you lazily define a property. Nothing is created until you reference the property, and the result is cached for quicker access in the future.
From the Kotlin documentation:
val lazyValue: String by lazy {
println("computed!")
"Hello"
}

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.