ono
ono

Reputation: 3104

Creating method to return generic parameterized class

Having a class:

abstract class Bird<T: Food>

and a subclass, where Fish extends Food:

class Falcon: Bird<Fish>()

How do I create a method that returns a Bird:

fun getBird(type: Int): Bird<Food> {
   if (type == SOME_CONSTANT) {
       return Falcon()
   } else {
      // Another bird
   }
}

Getting:

Type mismatch: Required Bird<Food>, found Falcon  

Upvotes: 0

Views: 120

Answers (2)

zsmb13
zsmb13

Reputation: 89668

You basically have two options to solve this:

  1. Using declaration site variance. This is preferred when possible.

    What you need for this to work is Bird<Fish> (which your Falcon is) to be a subtype of Bird<Food> (which you're returning). Since Fish is also a subtype of Food, what you need here is covariance, which you can specify for the type parameter with the out keyword:

    abstract class Bird<out T: Food>
    

    Now, you've added below in your comment that you have a mutable property of type T? in your base class, something like:

    abstract class Bird<out T: Food> {
        var food: T? = null
    }
    

    This is an issue for declaration site variance. If you want covariance to work, the type parameter can only be present in your class in out positions, meaning you can have functions that return T, but you can't have ones that accept T (in this case, that would be the setter of the property). Think about it: if you had a reference to a Falcon as a Bird<Food> because you got it from the getBird function, you could suddenly feed it any Food, not just Fish. This would break type safety. Which gets us to the other solution...

  2. Using use site variance.

    This is less ideal, but sometimes necessary (like apparently in your case, because of the mutable property of type T? in your base class. Credit for this solution goes to @tynn's very good answer, but let me expand on it.

    You can mark covariance on just the return type of the function instead of the entire abstract class by making the function return a Bird<out Food>.

    fun getBird(type: Int): Bird<out Food>
    

    Doing this will let you have your T parameter in both in and out positions in your class, but prevent you from using any functions that take T (have it in the in position) on the Bird instances that you get from the getBird function.

    So you could do this after the change:

    println(getBird(1).food)
    

    But not this for any value of newFood that's not null (since a variable of type Nothing? is required here, which only null is):

    getBird(1).food = newFood
    

    This restriction of not being able to set the food property of the Bird returned by the function is what "restores" type safety in this case.

Upvotes: 1

tynn
tynn

Reputation: 39873

You have to define the out variance on the return type of getBird()

fun getBird(type: Int): Bird<out Food>

Like this you can return birds with any food source without changing the Bird class.

Upvotes: 1

Related Questions