Create an instance from a generic parameter - kotlin

I want to refactor the following code so that I can reuse it with different ViewHolder types:
class TestsAdapter(
private val clickListener: ClickListener
) : PagedListAdapter<RecyclerViewTestInfo, TestsViewHolder>(diffCallback) {
override fun getItemCount(): Int {
return super.getItemCount()
}
override fun onBindViewHolder(holder: TestsViewHolder, position: Int) {
val testInfo = getItem(position) as RecyclerViewTestInfo
with(holder) {
bindTo(testInfo)
testInfo.let {
itemView.setOnClickListener {
clickListener(testInfo)
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestsViewHolder =
TestsViewHolder(parent)
}
It isn't clear to me though how you handle the creation of an instance for a generic type. In the code, onCreateViewHolder is initialized with a specific ViewHolder. How would I do this with a generic?

The problem you are facing here is that you can't directly initialize an instance from a generic type, you need to have at least a Class object. There is a solution, however I wouldn't recommend to use it, since it adds an undesirable layer of complexity.
Pass class type to constructor:
class Test<A : DiffUtil.Callback, B : RecyclerView.ViewHolder?>(type: Class<B>): PagedListAdapter<A, B>(diff)
There using reflection you will be able to create a new instance, however you need to know exactly the constructor, for example in your case you have a constructor with a single parameter of ViewGroup type:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): B =
type.getConstructor(ViewGroup::class.java).newInstance(parent)
This solution is undesirable, since there are no compile-time checks and when someone will create a new ViewHolder with a different constructor he will get a runtime error.

Related

Can you make a list of value classes, with the list being bound to the list of values?

Since value classes (aka inline classes) are not classes at runtime but the value class, is it possible to make a list of them, and the list is bound to the list of values?
If i couldn't explain that very clearly, here is a code example:
class Foo(var i)
#JvmInline
value class FooWrapper(val foo: Foo)
fun main() {
val fooList = mutableListOf(Foo(1), Foo(2))
val wrappedFooList = fooList.???<FooWrapper>()
// fooList and wrappedFooList are the same list at runtime, so when you insert a value to fooList, it gets "added" to wrappedFooList as `FooWrapper(added)`
// This is what im currently using, but this is a seperate list, so when a value gets inserted into fooList, it doesn't get inserted here.
val wrappedFooListButNotTheSameList = fooList.map { FooWrapper(it) }
fooList.add(Foo(3)) // FooWrapper(Foo(3)) now exists in wrappedFooList
}
Since value classes (aka inline classes) are not classes at runtime but the value class
This is only true sometimes. Inlining does not always happen, and the class file for the inline class does exist.
As soon as you start using generics, inlining goes out of the window. That is, your list of FooWrapper would not be inlined at all.
Documentation:
However, sometimes it is necessary to keep wrappers around. As a rule of thumb, inline classes are boxed whenever they are used as another type.
See also the code sample that follows that. This is likely because when they are used as another type, code that doesn't know about the inline class is likely going to be interacting with the wrapper, and unexpected behaviours would occur if they are not boxed.
With all that in mind, if you still want two lists of unrelated types, that are "linked" together, you can first encode the conversion between the types with an interface:
interface ConvertibleTo<T> {
val converted: T
}
data class Foo(var i: Int): ConvertibleTo<FooWrapper> {
override val converted get() = FooWrapper(this)
}
#JvmInline
value class FooWrapper(val foo: Foo): ConvertibleTo<Foo> {
override val converted get() = foo
}
Then make a ConvertList<T, U> and a ConvertListIterator<T, U> by delegating everything (yes this is a lot of boilerplate). The built-in by can't help here because you are also adding an extra .converted on every U value. Instead of the interfaces, you can also add T.() -> U and U.() -> T in the constructor parameters.
class ConvertList<T: ConvertibleTo<U>, U: ConvertibleTo<T>>(private val list: MutableList<T>): MutableList<U> {
override val size: Int
get() = list.size
override fun contains(element: U) = list.contains(element.converted)
override fun containsAll(elements: Collection<U>) =
list.containsAll(elements.map(ConvertibleTo<T>::converted))
override fun get(index: Int) =
list[index].converted
override fun indexOf(element: U) =
list.indexOf(element.converted)
override fun isEmpty() = list.isEmpty()
override fun iterator() = ConvertListIterator(list.listIterator())
override fun lastIndexOf(element: U) = list.lastIndexOf(element.converted)
override fun add(element: U) = list.add(element.converted)
override fun add(index: Int, element: U) = list.add(index, element.converted)
override fun addAll(index: Int, elements: Collection<U>) =
list.addAll(index, elements.map(ConvertibleTo<T>::converted))
override fun addAll(elements: Collection<U>) =
list.addAll(elements.map(ConvertibleTo<T>::converted))
override fun clear() = list.clear()
override fun listIterator() = ConvertListIterator(list.listIterator())
override fun listIterator(index: Int) = ConvertListIterator(list.listIterator(index))
override fun remove(element: U) = list.remove(element.converted)
override fun removeAll(elements: Collection<U>) =
list.removeAll(elements.map(ConvertibleTo<T>::converted))
override fun removeAt(index: Int) = list.removeAt(index).converted
override fun retainAll(elements: Collection<U>) =
list.retainAll(elements.map(ConvertibleTo<T>::converted))
override fun set(index: Int, element: U) = list.set(index, element.converted).converted
override fun subList(fromIndex: Int, toIndex: Int) = ConvertList(list.subList(fromIndex, toIndex))
}
class ConvertListIterator<T: ConvertibleTo<U>, U: ConvertibleTo<T>>(private val iter: MutableListIterator<T>): MutableListIterator<U> {
override fun hasPrevious() = iter.hasPrevious()
override fun nextIndex() = iter.nextIndex()
override fun previous() = iter.previous().converted
override fun previousIndex() = iter.previousIndex()
override fun add(element: U) = iter.add(element.converted)
override fun hasNext() = iter.hasNext()
override fun next() = iter.next().converted
override fun remove() = iter.remove()
override fun set(element: U) = iter.set(element.converted)
}
Usage:
val list = mutableListOf(Foo(1), Foo(2))
val list2 = ConvertList(list)

Kotlin generics supertype not applied

I was coding on Java for quite a long time and trying to migrate to Kotlin. I'm confused with Generics in Kotlin a bit...
I have a DelegateManager class. It should consume only subtypes of IViewData
class DelegateManager<T : IViewData> {
private val delegates: MutableList<AdapterDelegate<T>> = mutableListOf()
fun addDelegate(adapterDelegate: AdapterDelegate<T>) {
delegates.add(adapterDelegate)
}
...
}
Inside TrackListAdapter I want to add a delegate. As you might have seen it's AdapterDelegate<TrackViewData> and TrackViewData is a subtype of IViewData So it should work but it shows error inside init block of TrackListAdapter
class TrackListAdapter : BaseListAdapter<IViewData>() {
init {
delegateManager.addDelegate(TrackViewDelegate()) // error: Type mismatch -> Required: AdapterDelegate<IViewData>, Found: TrackViewDelegate
}
}
class TrackViewDelegate : AdapterDelegate<TrackViewData>() {
override fun onCreateViewHolder(parent: ViewGroup): ListViewHolder<TrackViewData> {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.track_item, parent, false)
return TrackViewHolder(itemView)
}
override fun isDelegateForDataType(data: IViewData) = data is TrackViewData
}
How to deal with it? How to extend the generic parameter correctly?

How do I cast custom MutableLiveData to custom LiveData?

suppose there are 2 classes:
class MyLiveData:LiveData<Int>()
class MyMutableLiveData:MutableLiveData<Int>()
Casting from MutableLiveData to LiveData is permitted:
val ld1=MutableLiveData<Int>()
val ld2:LiveData<Int> = ld1 //ok
But you can't cast your own implementations this way:
val mutable=MyMutableLiveData()
val immutable:MyLiveData = mutable //type missmatch
I understand that MutableLiveData extends LiveData thats why they are castable.But I can't have MyMutableLiveData extending MyLiveData as it won't be mutable in this case
Are there any workarounds?
UPD:I guess I need to show motivation of extending LiveData.I'm trying to implement MutableLiveDataCollection which notifies not just value changes via setValue/postValue but also value modification like adding new elements.I'm surprised there is no native solution for this.
Anyway to obseve modify events there have to be additional observe method.And this method have to be inside immutable part aka LiveDataCollection because views will call it.Inheritance is natural solution here IMHO.
The key idea sits in the MutableLiveData class.The only thing this class does - is it changes access modifiers on setValue/postValue methods.I can do the same trick.Therefore the final code will be:
open class LiveDataCollection<K,
L:MutableCollection<K>,
M:Collection<K>>: LiveData<L>() {
private var active=false
private var diffObservers = ArrayList<Observer<M>>()
fun observe(owner: LifecycleOwner, valueObserver: Observer<L>, diffObserver: Observer<M>) {
super.observe(owner,valueObserver)
diffObservers.add(diffObserver)
}
protected open fun addItems(toAdd:M) {
value?.addAll(toAdd)
if (active)
for (observer in diffObservers)
observer.onChanged(toAdd)
}
override fun removeObservers(owner: LifecycleOwner) {
super.removeObservers(owner)
diffObservers= ArrayList()
}
override fun onActive() {
super.onActive()
active=true
}
override fun onInactive() {
super.onInactive()
active=false
}
}
class MutableLiveDataCollection<K,L:MutableCollection<K>,
M:Collection<K>>: LiveDataCollection<K,L,M>() {
public override fun addItems(toAdd:M) {
super.addItems(toAdd)
}
public override fun postValue(value: L) {
super.postValue(value)
}
public override fun setValue(value: L) {
super.setValue(value)
}
}

Why Kotlin can not override List<*> operator method?

Here is my function:
operator infix fun List<Teacher>.get(int: Int): Teacher {
var t = Teacher()
t.name = "asd"
return t ;
}
and my usage:
b[0].teachers[1].name
tip: b is an object that has List< Teacher > property
and the errorEmpty list doesn't contain element at index 1.
why this override operator function doesn't work?
In Kotlin, you cannot shadow a member function with an extension. A member always wins in the call resolution. So, you basically cannot call an extension with a signature same to that of a member function, that is present in the type that was declared or inferred for the expression.
class C {
fun foo() { println("member") }
}
fun C.foo() { println("extension") }
C().foo() // prints "member"
In your case, the member function is abstract operator fun get(index: Int): E defined in kotlin.collections.List.
See the language reference: Extensions are resolved statically
As voddan mentions in the comment, you can't overshadow a method with an extension. However, there is a way to get around this with some polymorphism. I don't think I would recommend doing this in your case, but I guess it shows off a cool Kotlin feature.
If b[0] returns an object of type B, you could do this in that class:
data class B(private val _teachers: List<Teacher> = emptyList()) {
private class Teachers(private val list: List<Teacher>) : List<Teacher> by list {
override operator fun get(int: Int): Teacher {
var t = Teacher()
t.name = "asd"
return t ;
}
}
val teachers: List<Teacher> = Teachers(_teachers)
}
fun main(args: Array<String>) {
println(B().teachers[0].name) // Prints "asd"
}
When I override the get-function it will affect everyone that uses the B class, not just where you would import the extension-function.
Note that I am delegating all other method-calls on the Teachers-class through to the underlying list.

How do I select which Java overload to override in a Kotlin derived class?

I'm trying to implement in Kotlin an MyIntList extending java.util.AbstractList<Int>. AbstractList<E> defines both a E remove(int index) and a boolean remove(Object o). I use the following code:
class IntList() : AbstractList<Int>() {
....
override fun remove(index: Int): Int {
....
}
}
but this gives an error: return type of 'remove' is not a subtype of the return type of the overridden member 'public open fun remove(element: kotlin.Int!): kotlin.Boolean defined in java.util.AbstractList'
override fun remove(index: Int): Int {
How do I tell Kotlin that I'm trying to override E remove(int index)? (And where does it get that remove(Int):Boolean overload from?)
Turns out that very last sentence was the clue. There is some compiler shenanigans going on in that it thinks I'm implementing MutableList<Int>. And MutableList<E> defines a removeAt(element: E): Boolean which silently compiles into a remove method, changing the name. When I override removeAt everything works.