Kotlin - how to find an exact instance of a class - kotlin

sealed class Mission(open val quests: List<Quest>) {
sealed class SubMission(override val quests: List<Quest>) : Mission(quests) {
data class Mission1(override val quests: List<Quest>): SubMission(quests)
data class Mission2(override val quests: List<Quest>): SubMission(quests)
data class Mission3(override val quests: List<Quest>): SubMission(quests)
} }
I have this structure in my code and a List<Mission>.
Then using a following function I would like the list to return an exact item that matches the class given in function.
private fun getMission(currentMissionClass: KClass<Mission>): Mission {}
So let's say I'm giving the function argument Mission.SubMission.Mission1() and I expect the function to return the item from List<Mission> that matches that exact class. Is there an easy and convenient way to do this in kotlin?

filterIsInstance should work
private inline fun <reified T: Mission> getMission(
list: List<Mission>, currentMissionClass: Class<T>): List<T> {
return list.filterIsInstance<T>()
}
getMission(list, Mission1::class.java)

Related

Access Data class property while using Generic Type parameter <T> in runtime [Kotlin]

I'm trying to return a property/value from Data class by checking type parameter
Data Class :
data class SystemConfiguration(
val systemName: String,
val fields: List<String>
)
Abstract class :
abstract class ConfigurationLoader<out T> {
abstract val clazz: Class<out T>
private fun getAssociateAttribute(file: T): String{
return when(clazz){
SystemConfiguration::class.java -> file.systemName // Line causing error
// If its another Data class, I should return value from that data class
else -> ""
}
}
open fun loadConfigurations(): Map<String, T>{
val map = Paths.get(configurationFolderPath, filePath).toFile().walkTopDown().filter { it.isFile }.map {
val x = ionSystem.loader.load(it)[0]
ionValueMapper.readValue<List<T>>(x.toString())
}.flatten().associateBy { getAssociateAttribute(it) }
}
}
inline fun <reified T: Any> javaClasstype(): Class<T> {
return T::class.java
}
Sub class
class ServiceConfigurationLoader (
override val clazz: Class<SystemConfiguration> = javaClasstype()
): ConfigurationLoader<SystemConfiguration>()
I'm getting an exception "e: Unresolved reference: systemName". Not able to access values inside data class while we use type parameter
If i use like this(directly mentioning data class name), I'm able to access the values
private fun getAssociateAttribute(file: SystemConfiguration): String{
return when(clazz){
SystemConfiguration::class.java -> file.systemName
else -> ""
}
}
Could someone help me out here to access the value using Type paramater in Kotlin?
Thanks in Advance !!
I have tried using reified keyword as well. Still getting the same issue. I'm expecting to access the value using Type paramater

How to correctly declare/assign Kotlin generic interface and data class

I am investigating Kotlin Generics using Interfaces, Inner Interfaces and data classes.
I have declared the following:
interface MyInterface<X, Y> {
interface MyInnerInterface<Y> : MyInterface<Nothing, Y> {
val myY: Y
}
interface MySecondInnerInterface<X, Y> : MyInterface<X, Y> {
val myX: X
fun something(myY: Y): MyDataClass<Class<MySecondInnerInterface<X, Y>>, MyInnerInterface<Y>>
}
}
data class MyDataClass<A, B>(
val klass: Class<MyInterface.MySecondInnerInterface<A, B>>,
val myInnerInterface: MyInterface.MyInnerInterface<B>
)
I employ the above within a sealed class:-
sealed class Investigation<L, M> : MyInterface<L, M> {
data class MySecondInnerOne(override val myX: String) : MyInterface.MySecondInnerInterface<String, Long> {
private data class MyInnerOne(override val myY: Long) : MyInterface.MyInnerInterface<Long>
override fun something(myY: Long): MyDataClass<Class<MyInterface.MySecondInnerInterface<String, Long>>, MyInterface.MyInnerInterface<Long>>
= MyDataClass(this#MySecondInnerOne::class.java, MyInnerOne(myY)) // COMPILE ERROR!!!!!
}
The above generates a compile time error, most of which I do not understand as follows:-
I cannot see how to resolve all these errors.
What refactors are necessary to enable me to achieve the desired result?

How can I get the inner type of a generic class in Kotlin?

I know with Kotlin (on the JVM) I can get the type of a class with foo::class. And I know I can generic information, even at runtime, with typeOf<>() (as long as my function is inline). But I can't figure out how to get Int from a List<Int> for example.
Here's as far as I can get:
import kotlin.reflect.KType
import kotlin.reflect.typeOf
fun main() {
TypeTest.get<Int>()
TypeTest.get<List<Int>>()
TypeTest.get<Map<String, List<Int>>>()
}
class TypeTest {
companion object {
#OptIn(ExperimentalStdlibApi::class)
inline fun <reified OUT : Any> get() {
generateReturnType(typeOf<OUT>())
}
fun generateReturnType(type: KType) {
val classifier = type.classifier!!
println("class $classifier has parameters ${classifier::class.typeParameters}")
}
}
}
This prints:
class class kotlin.Int has parameters [T]
class class kotlin.collections.List has parameters [T]
class class kotlin.collections.Map has parameters [T]
Given a KClassifier, how can I get its inner type (ideally recursively, in the case of a List<List<Int>>)? Or is there another way to do this?

Kotlin class generics without duplication

Consider an abstract class:
abstract class PubSubSubscriber<T : Any>(private val topic: KClass<T>) : BackgroundFunction<PubSubMessage> {
abstract fun consume(payload: T)
override fun accept(message: PubSubMessage, context: Context) {
val json = String(Base64.getDecoder().decode(message.data.toByteArray()))
val payload = objectMapper.readValue(json, topic.java)
consume(payload)
}
}
And implementation:
class MySubscriber : PubSubSubscriber<Payload>(Payload::class) {
Is there a way to define such abstract class so that I don't have to repeat twice the Payload and Payload::class in the class definition?
Yes, with some reflection.
At construction time, we can extract the type parameter and assign it to a property that no longer needs to be given to the constructor:
abstract class PubSubSubscriber<T : Any> {
val topic: KClass<T> = extractTypeParam<T>(0).kotlin
private fun <X> extractTypeParam(paramIdx: Int): Class<X> {
require(PubSubSubscriber::class.java == javaClass.superclass) {
"PubSubSubscriber subclass $javaClass should directly extend PubSubSubscriber"
}
#Suppress("UNCHECKED_CAST")
return (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[paramIdx] as Class<X>
}
abstract fun consume(payload: T)
override fun accept(message: PubSubMessage, context: Context) {
val json = String(Base64.getDecoder().decode(message.data.toByteArray()))
val payload = objectMapper.readValue(json, topic.java)
consume(payload)
}
Note the following limitations:
A) this solution works only if MySubscriber directly extends from PubSubSubscriber. However, the given code can detect if that's not the case and warn about it (at runtime). In such cases, there are the following solutions:
MySubscriber falls back to providing a duplicate argument (essentially what you already had)
the direct superclass of MySubscriber can provide a similar detection mechanism
B) You call reflection code every time a MySubscriber instance is created. This may be too slow in certain contexts, but for many this is unproblematic.

Kotlin generics with in produces Type mismatch when compiling

I´m working on a code with generics and when I use an in I got a TypeMismatch when compiling.
The code is the following:
open class A
class B:A()
data class DataContainer(val a:String,
val b:A)
interface Repo<T:A>{
fun setParam(param:T)
fun getParam():T
}
abstract class RepoImp<T:A>:Repo<T>{
private lateinit var parameter:T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class BRepo:RepoImp<B>()
class Repo2(val repo: Repo<in A>){
fun process(b:DataContainer){
repo.setParam(b.b)
}
}
val repoB = BRepo()
val repo2 = Repo2(repoB)// Here I got: Type mismatch: inferred type is BRepo but Repo<in A> was expected
I also tried changing the attribute repo from Repo2 to Repo<*>
Since BRepo is a Repo<B>, it is not a Repo<in A>, (but it would satisfy Repo<out A>).
In other words, a Repo<in A> must be able to accept setParam(A()), but BRepo.setParam() can only accept a B or subclass of B.
Or to put it another way, BRepo is a Repo<B>, which is a tighter restriction on the type than Repo<A> when it comes to writing values (but looser restriction when reading values).
The reason class Repo2(val repo: Repo<*>) doesn't work is that Repo<*> is essentially a Repo<in Nothing/out A>. You can't call setParam() on a Repo<*> with any kind of object.
There's a design flaw in your code that you can't fix simply by changing Repo2's constructor signature. As it stands now, Repo2 needs to be able write A's to the object you pass to it, and a BRepo by definition does not support writing A's, only B's. You will need to make at least one of your class's definitions more flexible about types.
It might be easier to understand the covariance limitation with more common classes:
val stringList: MutableList<String> = ArrayList()
var anyList: MutableList<in Any> = ArrayList()
anyList.add(5) // ok
anyList = stringList // Compiler error.
// You wouldn't be able to call add(5) on an ArrayList<String>
Basically MutableList<String> is not a MutableList<in Any> the same way Repo<B> is not a Repo<in A>.
The Repo2 class expect to consume only type A, use Repo2<T : A>(val repo: Repo<in T>)
open class A
class B : A()
class C : A()
class D : A()
class BRepo : RepoImp<B>()
class CRepo : RepoImp<C>()
class DRepo : RepoImp<D>()
interface Repo<T : A> {
fun setParam(param: T)
fun getParam(): T
}
abstract class RepoImp<T : A> : Repo<T> {
private lateinit var parameter: T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class Repo2<T : A>(val repo: Repo<in T>) {
fun process(b: DataContainer<T>) {
repo.setParam(b.b)
}
}
data class DataContainer<T : A>(
val a: String,
val b: T
)
fun main() {
val repoB = BRepo()
val repoC = CRepo()
val repoD = DRepo()
val repo2 = Repo2(repoB)
val repo3 = Repo2(repoC)
val repo4 = Repo2(repoD)
repo2.process(DataContainer("Process B type", B()))
repo3.process(DataContainer("Process C type", C()))
repo4.process(DataContainer("Process D type", D()))
println(repo2.repo.getParam())
println(repo3.repo.getParam())
println(repo4.repo.getParam())
}