Kotlin generics with in produces Type mismatch when compiling - kotlin

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())
}

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

Cloning object of subclass type in Kotlin

I wanted to be able to define a method to clone an object that is the same type of itself. I define the interface requesting such, but the following does not compile or run.
interface Foo {
fun <T: Foo> copy() : T
}
class Bar(private val v:Int) : Foo {
override fun copy():Bar = Bar(v)
}
main() {
val bar1 = Bar(1)
val bar2 = bar1.copy()
}
If however I write the implementing class in Java, it will compile
class Bar implements Foo {
private int v;
public Bar(int v) {this.v = v;}
public Bar copy() {
return new Bar(v);
}
}
I can rewrite the code like the following that compiles:
interface Foo<out Foo>{
fun copy(): Foo
}
class Bar(private val v:Int) : Foo<Bar> {
override fun copy(): Bar = Bar(v)
}
However the following will fail with error: no type arguments expected for fun copy(): Foo
val newF = f.copy()
fun <T: Foo> addFoo(
foo: T,
fooList: List<T>,
): MutableList<T> {
val result: MutableList<T> = arrayListOf()
for (f in fooList) {
val newF = f.copy<T>()
result.add(newF)
}
result.add(foo)
return result
}
Is there a good solution to the problem?
The problem here is that Foo doesn't know the exact type of the implementing class, so has no way to specify that its method returns that same type.
Unfortunately, Kotlin doesn't have self types (see this discussion), as they would handle this situation perfectly.
However, you can get close enough by using what C++ calls the curiously-recurring template pattern. In Kotlin (and Java) you do this by defining Foo with a type parameter explicitly extending itself (including its own type parameter):
interface Foo<T : Foo<T>> {
fun copy(): T
}
Then the implementing class can specify itself as the type argument:
class Bar(private val v: Int) : Foo<Bar> {
override fun copy(): Bar = Bar(v)
}
And because T is now the correct type, everything else works out. (In fact, the : Bar is redundant there, because it already knows what the type must be.)
Your addFoo() method will then compile with only a couple of changes: give it the same type parameter <T: Foo<T>>, and remove the (now wrong, but unnecessary) type parameter when calling f.copy(). A quick test suggests it does exactly what you want (creates a list with clones of fooList followed by foo).
Since it's often useful for a superclass or interface to refer to the implementing class, this pattern crops up quite often.
BTW, your code is easier to test if Bar has its own toString() implementation, as you can then simply print the returned list. You could make it a data class, or you could write your own, e.g.:
override fun toString() = "Bar($v)"

Ktor reified type parametar

I created class with generic in kotlin and want to use receive with generic, but I have error when i want to call.recieve type from generic:
Can not use MType as reified type parameter. Use a class instead.
Code:
class APIRoute<EType : IntEntity, MType : Any> {
fun Route.apiRoute() {
post {
val m = call.receive<MType>()
call.respond(f(model))
}
}
}
How to fix it?
You need to provide the expected type to the receive() function. Due to type erasure in Java/Kotlin, the type of MType is unknown at runtime, so it can't be used with receive(). You need to capture the type as KType or KClass object when constructing APIRoute.
KClass is easier to use, however it works with raw classes only, it doesn't support parameterized types. Therefore, we can use it to create e.g. APIRoute<*, String>, but not APIRoute<*, List<String>>. KType supports any type, but is a little harder to handle.
Solution with KClass:
fun main() {
val route = APIRoute<IntEntity, String>(String::class)
}
class APIRoute<EType : IntEntity, MType : Any>(
private val mClass: KClass<MType>
) {
fun Route.apiRoute() {
post {
val m = call.receive(mClass)
call.respond(f(model))
}
}
}
Solution with KType:
fun main() {
val route = APIRoute.create<IntEntity, List<String>>()
}
class APIRoute<EType : IntEntity, MType : Any> #PublishedApi internal constructor(
private val mType: KType
) {
companion object {
#OptIn(ExperimentalStdlibApi::class)
inline fun <EType : IntEntity, reified MType : Any> create(): APIRoute<EType, MType> = APIRoute(typeOf<MType>())
}
fun Route.apiRoute() {
post {
val m = call.receive<MType>(mType)
call.respond(f(model))
}
}
}

Kotlin: generate a Factory by class

We're trying to do some generic processing in kotlin. Basically, for a given class, we want to get the related Builder object. i.a. for any object that extends a GenericObject, we want a Builder of that Object.
interface Builder<T : GenericObject>
object ConcreteBuilder: Builder<ConcreteObject>
We'd need a function that will return ConcreteBuilder from ConcreteObject
Our current implementation is a Map:
val map = mapOf<KClass<out GenericObject>, Builder<out GenericObject>>(
ConcreteObject::class to ConcreteBuilder
)
Then we can get it with:
inline fun <reified T : GenericObject> transform(...): T {
val builder = map[T::class] as Builder<T>
...
However this isn't very nice as:
we need an explicit cast to Builder<T>
the map has no notion of T, a key and a value could be related to different types.
Is there any better way to achieve it?
A wrapper for the map could be:
class BuilderMap {
private val map = mutableMapOf<KClass<out GenericObject>, Builder<out GenericObject>>()
fun <T: GenericObject> put(key: KClass<T>, value: Builder<T>) {
map[key] = value
}
operator fun <T: GenericObject> get(key: KClass<T>): Builder<T> {
return map[key] as Builder<T>
}
}
This hides the ugliness, while not completely removing it.
To use:
val builderMap = BuilderMap()
builderMap.put(ConcreteObject::class, ConcreteBuilder)
builderMap.put(BetonObject::class, BetonBuilder)
// builderMap.put(BetonObject::class, ConcreteBuilder) – will not compile
val builder = builderMap[T::class]

Kotlin: How can I get the delegation class of a member property?

How can I get the delegation class of a member property?
By this, I mean is it possible to complete such a function:
inline fun <reified T> delegationExample(t: T) {
for (prop in T::class.declaredMemberProperties) {
val delegatedClass = // what to do?!
}
}
Where the delegation class may look like:
class DelegationExample {
operator fun getValue(ref: Any, prop: KProperty<*>) = 0
}
And the declaration class might look like this:
object Example {
val a by DelegationExample()
val b by DelegationExample()
val c by DelegationExample()
}
To find properties that delegate to a delegate class along with the instance of that class, here is a utility function:
data class DelegatedProperty<T : Any, DELEGATE : Any>(val property: KProperty1<T, *>, val delegatingToInstance: DELEGATE)
inline fun <reified T : Any, DELEGATE : Any> findDelegatingPropertyInstances(instance: T, delegatingTo: KClass<DELEGATE>): List<DelegatedProperty<T, DELEGATE>> {
return T::class.declaredMemberProperties.map { prop ->
val javaField = prop.javaField
if (javaField != null && delegatingTo.java.isAssignableFrom(javaField.type)) {
javaField.isAccessible = true // is private, have to open that up
#Suppress("UNCHECKED_CAST")
val delegateInstance = javaField.get(instance) as DELEGATE
DelegatedProperty(prop, delegateInstance)
} else {
null
}
}.filterNotNull()
}
A few notes:
First correct your reified type T to T: Any or you cannot access all of the extensions in Kotlin reflection including declaredMemberProperties
It is easiest to get to the field from a property reference to be sure you are actually talking about something that is really a property, so for each of declaredMemberProperties use javaField to do so.
Since javaField is a custom getter and could be nullable, it is saved to a local variable so smart casting will work later.
Then if this field has the same type as the delegation class you are looking for, you can then access the field.
But first you have to force the field's accessibility because it is a private field.
Running this in test program:
class DelegationExample {
operator fun getValue(ref: Any, prop: KProperty<*>) = 0
}
class Example {
val a by DelegationExample()
val b by DelegationExample()
val c by DelegationExample()
}
fun main(args: Array<String>) {
findDelegatingPropertyInstances(Example(), DelegationExample::class).forEach {
println("property '${it.property.name}' delegates to instance of [${it.delegatingToInstance}]")
}
}
The output is something like:
property 'a' delegates to instance of [DelegationExample#2c1b194a]
property 'b' delegates to instance of [DelegationExample#4dbb42b7]
property 'c' delegates to instance of [DelegationExample#66f57048]
One way to avoid reflection is to first initialize your delegate object and store it as a member of its own, and then delegate your property by it.
object Example {
val aDelegate = DelegationExample()
val bDelegate = DelegationExample()
val cDelegate = DelegationExample()
val a by aDelegate
val b by bDelegate
val c by cDelegate
}
On the byte code level delegated properties do not defer from regular ones (public getter/setter and a private field).
One way you could go is scanning the private fields of Example and filtering those which have operator getValue(thisRef: R, KProperty<*>). Technically a field may contain a delegate object val x = lazy {1}, but that is not very likely.