Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incompatible types with LiveData of a child as a LiveData of a parent

I want to use MutableLiveData to observe some data from a ViewModel. The problem is that I use child and parent class, and there is some incompatibility with LiveData. An example of what I want to do in Kotlin:

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel

class Test : ViewModel() {

    abstract class Parent(protected var id: Int)

    class ChildFirst(id: Int) : Parent(id)

    class ChildSecond(id: Int) : Parent(id)

    var childFirst : MutableLiveData<ChildFirst> = MutableLiveData<ChildFirst>()

    var childSecond : MutableLiveData<ChildSecond> = MutableLiveData<ChildSecond>()

    var shouldManageFirstChild = true

    fun returnCorrectChild(): MutableLiveData<Parent> {
        if (shouldManageFirstChild) {
            return childFirst //won't compile in Android Studio (Type mismatch)
        } else {
            return childSecond as MutableLiveData<Parent> //compile and work with a warning in AndroidStudio (Unchecked cast)
        }
    }
}

And here is in Java :

import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;

public class Test extends ViewModel {
    class Parent {
        protected int mId;

        Parent(int id) {
            mId = id;
        }
    }

    class ChildFirst extends Parent {
        ChildFirst(int id) {
            super(id);
        }
    }

    class ChildSecond extends Parent {
        ChildSecond(int id) {
            super(id);
        }
    }

    MutableLiveData<ChildFirst> childFirst = new MutableLiveData <ChildFirst>();

    MutableLiveData<ChildSecond> childSecond = new MutableLiveData <ChildSecond>();
    boolean shouldManageFirstChild = true;

    MutableLiveData<Parent> returnCorrectChild(){
        if (shouldManageFirstChild) {
            return childFirst; //won't compile in Android Studio (Incompatible type)
        } else {
            return (MutableLiveData<Parent>) childSecond;   //won't compile in Android Studio (Inconvertible cast)
        }
    }
}

As you see, the problem is that the compiler don't consider the type the same for MutableLiveData<Child> and MutableLiveData<Parent>.

In Kotlin, I can cast the LiveData of a child as a parent. Even if there is a warning, the code run as intended: I can observe a MutableLiveData<Parent>.

Worse, in Java, it's impossible to compile, even with a cast.

So here is my question:

  • Why I can't use the LiveData of a child as the LiveData of a parent ? Is that something intended by the LiveData ?

  • Are they some eventual consequences of casting it with the kotlin 'as' ?

like image 313
EPuillandre Avatar asked Nov 08 '22 00:11

EPuillandre


1 Answers

Why I can't use the LiveData of a child as the LiveData of a parent ? Is that something intended by the LiveData ?

In java the answer is quite simple: generic types in Java are invariant, meaning that List<String> is not a subtype of List<Object> (quoted from kotlin docs here)

Are they some eventual consequences of casting it with the kotlin 'as' ?

Kotlin is simply doing a compile time warning, but what happens in runtime is that your check is only agains the non generics part. That's why, I think (and if anyone know better than me, please explain! I'm courios about this), you can do it in Kotlin.

For a better explanation of what happens in kotlin (and also in Java) while using the generics type i recommend you to read the the full kotlin docs about generics here There is even plenty of articles about java generics casting like this one

EDIT: A way for resolving your problem could be this: declare a single live data and treat the childs in a simple manner like this:

val liveParent = MutableLiveData<Parent>()
val childFirst = ChildFirst()
val childSecond = ChildSecond()

and then return the correct child in the liveParent while calling returnCorrectChild()

fun returnCorrectChild() {
    if (shouldManageFirstChild) {
        liveParent.value = firstChild
    } else {
        liveParent.value = secondChild
    }
}
like image 67
Pietro Scarampella Avatar answered Nov 14 '22 23:11

Pietro Scarampella