How to return child object if generic type extends parent class?


#1

Here is something I am trying to achieve.
I have one abstract class called BaseModel and also have CommonModel class which extends BaseModel.

I have added following function in my android app source code

fun <T: BaseModel> execute():T{
return CommonModel()
}

but this is giving me Type mismatch error by saying
Required T
found CommonModel

My query is, is it possible to return child object when generic type extends parent class? if yes, how i can achieve it?


#2

I might be wrong on this, but try:

fun <out T: BaseModel> execute():T{
    return CommonModel()
}

#3

Its saying Variance annotations are only allowed for type parameters of classes and interfaces


#4

Like this:

  fun <T : BaseModel> execute(): T {
        return CommonModel()  as T
    }

because the actul type for T is decided , just when execute is called like below , at this time T is ComonModel, so the result must be casted to T

val result : CommonModel = execute()


#5

Yes this is working… thanks:+1:


#6

Please do not use that solution. You will find that the compiler warns against unchecked cast.

The problem is that your method should not be generic. By making it generic you are promising that a caller can substitute T for any subclass of BaseModel. For instance, this compiles but throws exception at runtime:

class CustomClass: BaseModel

val result = execute<CustomClass>()

The correct solution would be to make the function non-generic and simply declare CommonModel as the return type. Or if you want to hide the actual implementation, you can make BaseModel the return type.


#7

I can not make this function non-generic. code i posted is just sub-set of my function actual function is 20+ lines long. at the end I need to return object with satisfying T: BaseModel condition


#8

I do not see how that prevents you from declaring the return type simply as BaseModel. The purpose of generics is to allow the user to specify the actual type substituting the type parameter. If the actual type is decided by the function, then it should not be generic.


#9

@Varia I can not return BaseModel directly. More clarification regarding this, I am using this for returning Network response so BaseModel contains status object which is common across all response and CommonModel is just one of classes which are inherited from BaseModel. There are different model classes (one for each request/response). I am planning to return CommonModel when it is not possible to return specific (T) class.


#10

I still think there has to be a better way. Casting to a type parameter is a code smell and in your example is not type safe. I will need to see a more complete example in order to understand what is going on. In particular, I need to see the full signature of the function and I need to see the part of the function that returns anything other than CommonModel.


#11

ok it goes like this

abstract class BaseModel {
    @SerializedName("code")
    var status: String? = null

    @SerializedName("message")
    var message: String? = null
}
class CommonModel : BaseModel()
class UserModel: BaseModel(){
     @SerializedName("first_name")
     var firstName: String? = null
}

Say I want UserModel (there are also other model classes for different network calls) as response but somehow I received an error so I can create instance of CommonModel and return it instead of UserModel.


#12

Let your function return BaseModel in its signature. You can still return a UserModel if everything goes fine, or CommonModel if there is an error.

At the call site, you can check the type of the returned object. Ideally, you should declare the BaseModel as a sealed class.


#13

You might like to look at the case study in section 8.8 of chapter 8 of some Kotlin programming notes I prepared. The notes can be found here.


#14

Why would you want to even return a model if the request failed? Usually exceptions are used to indicate errors. In case errors are common enough that you want to force the caller to handle them, you can declare the return type as either T? or Result<T>, and return respectively null or Result.failure to indicate error.


#15

It’s actually (a type of) good programming practice to return a model that models the error. If you have a view that needs to display data from the server, then you request data and get some model that represents the state. If the network request was successful, it has a model of that data. If it failed, it has a model of how to represent a failure. It’s deterministic.


#16

@mattquigley I couldn’t agree more


#17

@kenbarc Thanks for link. I will go through it