Unable to implement Generic interface function in kotlin - android

I have created interface to separate domain from data layer.
interface EditRepository<T> : Repository {
fun <P, R> post(postRequest: P): LiveData<ResponseData<R>>
fun <E, R> edit(editRequest: E): LiveData<ResponseData<R>>
fun <D, R> delete(deleteRequest: D): LiveData<ResponseData<R>>
}
Each function should have it's own generic types(request and response)
After i try to implement this interface methods , I replace generic types with my own types and seem they are not working as it is unable to import my type inside angle brackets of new overriden function(pointed below on screenshot). And i do not know what I am doing wrong here. Why i can't override generic interface function?
If i try manually import my type, it refuses to accept my function as overiden

You can't change type of override function-level generic. As workaround you can make class cast
override fun <P, R> post(postRequest: Response<P>): LiveData<Response<R>> = liveData(IO){
........
emit(ResponseData.SuccessResponse(...) as ResponseData<R>)
}
but you'll get lint warning Unchecked cast:
In other hand you can use type-level generic
interface EditRepository<D,E,P,R> : Repository {
fun post(postRequest: P): LiveData<ResponseData<R>>
fun edit(editRequest: E): LiveData<ResponseData<R>>
fun delete(deleteRequest: D): LiveData<ResponseData<R>>
}
and specify required types. But with Retrofit type-level generics causes IllegalArgumentException: Type parameters are unsupported

Related

Kotlin : Casting an object to Generic class

How get the type of a Generic class and cast an object to it?
I want to use this function to pass an Interface class:
protected fun <T> getInteraction(): T {
return when {
context is T -> context
parentFragment is T -> parentFragment as T
else -> throw IllegalArgumentException("not implemented interaction")
}
}
and use it like:
private var interaction: ISigninInteraction? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
interaction = getInteraction<ISigninInteraction>()
}
Kotlin, Java and JVM do have types erasure when Generics are implemented. Generics are not visible at the bytecode level. It means, you cannot use type parameters, e.g. T, directly in the function code.
Kotlin adds support for reified generics, which helps here. You may declare the function like
inline fun <reified T> getIt() : T {
...
}
With the help of reified and inline it will be possible to cast to T and return it.
https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters
The second alternative is to follow a Java practice - add the Class<T> parameter to the function and use Class#cast to cast to T instead.
You may combine the reified inline function with the approach like:
inline fun <reified T> getIt() = getIt(T::class.java)

Kotlin Inheritance with Boolean generics

I have a problem when I try to override a generics method with Boolean,Double,Integer,Float.
It works with Date. (May because is it Serializable?)
The interface:
interface AnInterface<C, T> {
fun doSomething(items: List<T>, vararg value: C): List<T>
}
An abstact implementation: (No override doSomething)
abstract class BaseClass<C, T> : AnInterface<C, T> { ... }
It's work:
class AnImplementetion<T> : BaseClass<Date, T>() {
override fun doSomething(items: List<T>, vararg value: Date): List<T> {
// It works
}
}
It doesn't work:
class AnAnotherImplementetion<T> : BaseClass<Boolean, T>() {
override fun doSomething(items: List<T>, vararg value: Boolean): List<T> {
// It doens't
}
}
The IDE always want to implement the doSomething. When I implement it with IDE it creates always the same one.
Error message:
Class 'AnAnotherImplementetion' is not abstract and does not implement abstract base class member
public abstract fun fun doSomething(items: List<T>, vararg value: Boolean): List<T> defined in BaseClass
'doSomething' overrides nothing
How can I fix it?
Thank you
UPDATE:
It works with JAVA. But Why doesn't with Kotlin?
public class AnAnotherImplementetion<T> extends BaseClass<Boolean, T> {
#NotNull
#Override
public List<T> doSomething(#NotNull List<? extends T> items, Boolean... value) {
// It works with JAVA
}
}
UPDATE 2:
It works when vararg is nullable.
interface AnInterface<C, T> {
fun doSomething(items: List<T>, vararg value: C?): List<T>
}
It looks like a bug in kotlin compiler. As I know during compiling it decides to use primitive type (int) or wrap (Integer). Java generics can't work with primitives, so compiler uses wrap for generic-type, BUT then compiler sees, that method param is never null and replaces it with primitive-type, and type-conflict appears. And here nullable saves a day.
But I'm not sure, it's just a guess.
The kotlin reference regarding basic types contains a passage which explains how it should deal with primitive types and generics in particular.
Obviously the latter is not working correctly. When generics are involved it should box the types which it either doesn't do or which the compiler complains about falsely.
You should open a bug at https://youtrack.jetbrains.com/ and link it here too. Maybe it was also a conscious design decision.
A workaround is to use the nullable type Boolean? as that will work as it is described in the reference. It will be boxed and therefore will work with generics.
Alternatively, if you are on the JVM, you can use the java.lang.Boolean instead. It's the object type of the primitive boolean and even though it is discouraged to use the Java types in Kotlin it is a possible workaround until Kotlin behaves as it should. However... testing it, Kotlin does some more magic around it so that its usage isn't that helpful neither. You would then even need to cast the java.lang.Boolean.TRUE as java.lang.Boolean. That's clearly not helpful at all. Opening a bug is the best you can do here.

Kotlin extension on interface

Let's say I have an interface Base and that we implement that interface in class Base1 : Base.
I would expect an extension function in the form
fun ArrayList<Base>.myFun()
to also work on arrayListOf(Base1(), Base1()).myFun(), but it doesn't. It requires the list to be of type Base instead of Base1.
Isn't this really strange? Or am I just missing something?
And, what are my options to write a function available on all subclasses of an interface?
Thanks!
You need to allow extension function to accept child implementation
interface Base
class Base1: Base
fun ArrayList<out Base>.myFun() = println(toString())
fun main(args: Array<String>) {
arrayListOf(Base1(), Base1()).myFun()
}
Lets say that you have:
interface Base
class Base1: Base
class Base2: Base
To have a fun for each class that extends your interface, you should create a class with a generic extension:
fun <T : Base> ArrayList<T>.myFun() = arrayListOf<T>()
In this case what you are doing is do an extension on any ArrayList which container class would be any class that extends from your Base interface. In the same way it will return an arraylist of that type.
In code this will look like:
fun myCode(){
val array1 : ArrayList<Base1> = arrayListOf(Base1()).myFun()
val array2 : ArrayList<Base2> = arrayListOf(Base2()).myFun()
}
You can read more about it in the official documentation, it has a lot of generic examples too: https://kotlinlang.org/docs/reference/extensions.html
This:
fun ArrayList<Base>.myFun()
is an extension only on ArrayList<Base>.
You can make the function generic to support any implementers of Base:
fun <T : Base> ArrayList<T>.myFunc() {}
Usage:
ArrayList<Base1>().myFunc()
This should work with array list of any type:
fun <T> ArrayList<T>.myFun()

Function with Generic object as parameter

How to create method that takes generic object as parameter?
Like object of class Apple or Cycle. I don't want to downcast it from Any.
fun putObject(y: <T> /*and even only "T"*/) {
}
According to the documentation:
Not only classes can have type parameters. Functions can, too. Type parameters are placed before the name of the function:
fun <T> singletonList(item: T): List<T> {
// ...
}
You have to declare that T is a type parameter. You have to do it before the name of the function (like you would in Java). Rewriting your code as follows works:
fun <T> putObject(y: T) {
// ...
}

Kotlin One type argument expected for class for abstract generic view holder

I'm trying to create a RecyclerView.Adapter with the following:
View holders - Provided the abstract parent and one son
abstract class BaseSettingsViewHolder<T>(var viewDataBinding :
ViewDataBinding) : RecyclerView.ViewHolder(viewDataBinding.root) {
abstract fun onBind(data: T, presenter: ISettingsPresenter, position: Int)
}
class SettingsTitleViewHolder(viewDataBinding: ViewDataBinding) : BaseSettingsViewHolder<TitleData>(viewDataBinding) {
override fun onBind(data: TitleData, presenter: ISettingsPresenter, position: Int) {
viewDataBinding.setVariable(BR.titleData, data)
viewDataBinding.setVariable(BR.itemPosition, position)
viewDataBinding.setVariable(BR.settingsPresenter, presenter)
viewDataBinding.executePendingBindings()
}
}
And when trying to declare the adapter:
class SettingsAdapter(var context: Context, var presenter: ISettingsPresenter) : RecyclerView.Adapter<BaseSettingsViewHolder>() {
I'm getting "One type argument expected for class" compile error on the:
RecyclerView.Adapter<BaseSettingsViewHolder>
Appreciate the help!
1- If you use always SettingTitleViewHolder;
RecyclerView.Adapter<SettingTitleViewHolder>
2- If you want to use any class which extended BaseSettingViewHolder;
RecyclerView.Adapter<BaseSettingsViewHolder<*>>
3- Use Any Object Type
RecyclerView.Adapter<BaseSettingsViewHolder<Any>>
4- Use extended type parameter for adapter class
class SettingsMenuAdapter<T>:RecyclerView.Adapter<T> where T : BaseSettingViewHolder<*>
5- Define out for abstract class type parameter.
abstract class BaseSettingsViewHolder<out T> RecyclerView.Adapter<BaseSettingsViewHolder<Any>>
7- Read that article =>
"Generics in Kotlin" ;)
You should specify the type argument for the BaseSettingsViewHolder in the RecyclerView.Adapter<BaseSettingsViewHolder> type.
Kotlin, unlike Java, does not have the raw types, so you cannot simply omit the type arguments.
The closest concept to raw types is star-projected types: use BaseSettingsViewHolder<*> in RecyclerView.Adapter<BaseSettingsViewHolder<*>>, this will mean that the type argument for BaseSettingsViewHolder is unknown.

Categories

Resources