bajicdusko
bajicdusko

Reputation: 1650

Kotlin generics inheritance - Type mismatch

I am trying to build a set of providers for realm objects. Here is an example structure I've tried to build:

Interface:

interface IDataProvider<out T : RealmObject> {
     fun getRealmObject(): T
}

Base provider class with companion function for typed provider instantiation:

open abstract class BaseProvider<out T : RealmObject> constructor(protected val context: Context?) : IDataProvider<T> {

     companion object {
         fun <T : RealmObject, E : BaseProvider<T>> create(context: Context?): E {        
        if (something) {
            return SomeChildProviderProvider(context)
        } else {
            throw TypeNotSupportedException()
        }
      }
    }
 }

And here is a child class:

class SomeChildProvider(context: Context?) : BaseProvider<ChildRealmModel>(context){
    override fun getRealmObject(): ChildRealmModel {
         throw UnsupportedOperationException("not implemented")
    }
}

Problem I have is on the line

return SomeChildProviderProvider(context)

Type mismatch. Required: E. Found: SomeChildProvider.

I can't figure out why it does not see that E is actually SomeChildProvider. Thank you.

P.S. I know that I can cast it to E, but in my opinion, it should not be needed in this situation. Maybe I am missing something obvious here or probably lack of Kotlin knowledge.

UPDATE1: After the first answer, we have realized that code above does not make much sense since we have to define a type of returning provider and to pass it into create method. Initial idea was that create method returns some type which is BaseProvider subtype. Here are the changes I have made in order to support the initial idea:

IDataProvider

interface IDataProvider {
    fun execute(realm: Realm)
    fun createModel(realm: Realm): RealmObject
}

BaseProvider

 open abstract class BaseProvider constructor(protected val context: Context?) : IDataProvider {

    override fun execute(realm: Realm) {
        realm.executeTransaction { r ->
            createModel(r)
        }
    }

    companion object {
        fun create(context: Context?): IDataProvider {
            if (something) {
                return ChildProvider(context)
            } else {
                throw TypeNotSupportedException()
            }
        }
    }
}

ChildProvider

class ChildProvider(context: Context?) : BaseProvider(context) {
    override fun createModel(realm: Realm): ChildRealmModel {
        var realmObject = realm.createObject(ChildRealmModel ::class.java)
        //object property initialization
        return realmObject
    }
}

UI call

BaseProvider.create(context).execute(realm)

Although, createModel method returns RealmObject, it's instance will be of ChildRealmModel. What I don't like about it is that we have to inspect instance type and cast into if we need exact model somewhere else.

Upvotes: 1

Views: 2190

Answers (1)

voddan
voddan

Reputation: 33829

Your code is not consistent.

In the function declaration you pledge to return E, which is a subtype of BaseProvider<T> and can be chosen by the user on the call site.

But in the implementation you return SomeChildProviderProvider, which is of course a subtype of BaseProvider<T>, but still can be totally unrelated to E which was chosen by the user.

An example:

class AnotherChildProvider : BaseProvider<ChildRealmModel>(context) {...}

val x = BaseProvider.create<ChildRealmModel, AnotherChildProvider>(context)

What is the type of x? According to the function signature, it must be AnotherChildProvider. But inside the function you return SomeChildProviderProvider, which CAN NOT be casted to AnotherChildProviderProvider.

Upvotes: 1

Related Questions