Is it possible to use all possible types from reflection as generic type? - kotlin

I am trying to use reflection to automatically create savedStateHandle for some classes.
fun <T> KProperty1<T, *>.argFrom(handle: SavedStateHandle) =
when (this.returnType.javaType) {
String::class.java -> handle.get<String>(this.name)
Int::class.java -> handle.get<Int>(this.name)
Uri::class.java -> handle.get<Uri>(this.name)
else -> throw RuntimeException("Type not implemented yet")
}
As you see IF the type is for instance String then I want the return type of get function to be String and when doing it this way I have to define every single case manually.
It would be nice if I could just do something like this.
fun <T> KProperty1<T, *>.argFrom(handle: SavedStateHandle) =
handle.get<this.returnType.javaType>(this.name)

You need a type parameter for the return type of the property. Then you can use that type parameter to specify the desired return type of get:
fun <T, V> KProperty1<T, V>.argFrom(handle: SavedStateHandle) =
handle.get<V>(this.name)

Related

Add Extension function in kotlin to all classes

Is it possible to add extension function to all classes? I was thinking about adding it to some common base class like Object. Is it possible?
With Kotlin, Any is the super type like Object for Java.
fun Any.myExtensionFunction() {
// ...
}
And if you want to support null-receiver too:
fun Any?.myExtensionFunction() {
// ...
}
It depends on whether you want to use the type of the object in the signature (either in another argument, or in the return type). If not, use Any(?) as Kevin Robatel's answer says; but if you do, you need to use generics, e.g. (from the standard library)
inline fun <T, R> T.run(block: T.() -> R): R
inline fun <T> T.takeIf(predicate: (T) -> Boolean): T?
etc.

Generic function in Kotlin

In Kotlin you can have a generic function like this:
fun <T> singletonList(item: T): List<T> {
// ...
}
I don't understand what the purpose of the <T> after the fun keyword is for. The function returns List<T>, so what is the point of <T>?
To be able to create a generic function the compiler must know that you want to work with diferent types. Kotlin is (like Java or C#) a strongly typed language. so just passing different types into a function will make the compiler mad.
To tell the compiler that a function should be accepting multiple types you need to add a "Type Parameter"
The <T> after fun is the definition of said "Type Parameter".
Which is then used at the item Argument.
Now the compiler knows that you'll specifiy the type of item when you make the call to singletonList(item: T)
Just doing
fun singletonList(item: T) : List<T> {[...]}
would make the compiler unhappy as it does not know T.
(As long as you don't have a class named T)
You also can have multiple "Type Params" when you separate them with commas:
fun <T, U> otherFunction(firstParam: T, secondParam: U): ReturnType
This is a generic function which, as per the language's syntax requirements, needs to provide this part <T>. You can use it to specify T further:
fun <T: Number> singletonList(item: T): List<T> {
// ...
}
It's also common to have multiple generic types:
fun <T: Number, R: Any> singletonList(item: T): R {
// ...
}

obtain class from nested type parameters in kotlin

I have a val built like this
val qs = hashMapOf<KProperty1<ProfileModel.PersonalInfo, *> ,Question>()
How can I obtain the class of ProfileModel.PersonalInfo from this variable?
In other words what expression(involving qs of course) should replace Any so that this test passes.
#Test
fun obtaionResultTypeFromQuestionList(){
val resultType = Any()
assertEquals(ProfileModel.PersonalInfo::class, resultType)
}
Thank you for your attention
There is no straight way to get such information due to Java type erasure.
To be short - all information about generics (in your case) is unavailable at runtime and HashMap<String, String> becomes HashMap.
But if you do some changes on JVM-level, like defining new class, information about actual type parameters is kept. It gives you ability to do some hacks like this:
val toResolve = object : HashMap<KProperty1<ProfileModel.PersonalInfo, *> ,Question>() {
init {
//fill your data here
}
}
val parameterized = toResolve::class.java.genericSuperclass as ParameterizedType
val property = parameterized.actualTypeArguments[0] as ParameterizedType
print(property.actualTypeArguments[0])
prints ProfileModel.PersonalInfo.
Explanation:
We define new anonymous class which impacts JVM-level, not only runtime, so info about generic is left
We get generic supperclass of our new anonymous class instance what results in HashMap< ... , ... >
We get first type which is passed to HashMap generic brackets. It gives us KProperty1< ... , ... >
Do previous step with KProperty1
Kotlin is tied to the JVM type erasure as well as Java does. You can do a code a bit nice by moving creation of hash map to separate function:
inline fun <reified K, reified V> genericHashMapOf(
vararg pairs: Pair<K, V>
): HashMap<K, V> = object : HashMap<K, V>() {
init {
putAll(pairs)
}
}
...
val hashMap = genericHashMapOf(something to something)

In Kotlin, how do I idiomatically access nullable nested map values, or return a default?

Quick Kotlin best practices question, as I couldn't really work out the best way to do this from the documentation.
Assume I have the following nested map (typing specified explicitly for the purpose of this question):
val userWidgetCount: Map<String, Map<String, Int>> = mapOf(
"rikbrown" to mapOf(
"widgetTypeA" to 1,
"widgetTypeB" to 2))
Can the following mode be any more succinct?
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username]?.get(widgetType)?:0
}
In other words, I want to return the user widget count iff the user is known and they have an entry for that widget type, otherwise zero. In particular I saw I can use [] syntax to access the map initially, but I couldn't see a way to do this at the second level after using ?..
I would use an extension operator method for that.
// Option 1
operator fun <K, V> Map<K, V>?.get(key: K) = this?.get(key)
// Option 2
operator fun <K, K2, V> Map<K, Map<K2, V>>.get(key1: K, key2: K2): V? = get(key1)?.get(key2)
Option 1:
Define an extension that provides get operator for nullable map. In Kotlin's stdlib such approach appears with Any?.toString() extension method.
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username][widgetType] ?: 0
}
Option 2:
Create a special extension for map of maps. In my opinion, it is better because it shows the contract of the map of maps better than two gets in a row.
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username, widgetType] ?: 0
}

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.