Method Covariance in Scala
Consider these base classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Dirt | |
class GardenItem extends Dirt { | |
val flavor = "seedy" | |
} | |
class Cucumber extends GardenItem { | |
override val flavor = "bland" | |
} | |
class Watermelon extends GardenItem { | |
override val flavor = "sweet" | |
} | |
class Cocktail extends Cucumber { | |
override val flavor = "refreshing" | |
} | |
class FruitSalad extends Watermelon { | |
override val flavor = "fruity" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Here is an object that has a method mixFoods which will accept | |
a subclass of a GardenItem and return a list of either | |
GardenItems or subclasses of GardenItems. | |
*/ | |
object CovarianceExample { | |
/** | |
Covariance example (compiles) | |
def mixFoods[T >: GardenItem](food: T): List[T] = { | |
This method definition states that within the context of the | |
mixFoods method, there is a type T that is a superclass of a GardenItem. | |
We then go on to say that this method accepts an argument which | |
is of type T, a subclass of GardenItem and it returns a List of | |
either GardenItems or subclasses of GardenItems. | |
*/ | |
def mixFoods[T >: GardenItem](food: T): List[T] = { | |
food match { | |
case x: FruitSalad => { | |
/** | |
Watermelon is a subclass of GardenItem so this is valid | |
to the compiler. | |
*/ | |
List(new Watermelon, food) | |
} | |
case x: Cucumber => { | |
/** | |
Cocktail is a subclass of Cucumber, which is a subclass of | |
GardenItem so this is also valid to the compiler. | |
*/ | |
List(food, new Cocktail) | |
} | |
case _ => List() | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Here's an example of failing to return the correct type | |
*/ | |
object CovarianceExampleFailFirst { | |
/** | |
Covariance example (does not compile) | |
def mixFoods[T >: Watermelon](food: T): List[T] = { | |
This method definition states that within the context of | |
the mixFoodsFail method, there is a type T that is either | |
a Watermelon or a subclass of Watermelon. | |
Given what we know about type T, this method will accept an argument | |
of type T and return a list of type T. | |
Unfortuanately, this code will not compile since we are | |
attempting to return a GardenItem within the list of what | |
can only contain type T. | |
*/ | |
def mixFoods[T >: Watermelon](food: T): List[T] = { | |
food match { | |
case x: FruitSalad => { | |
/** | |
This will not compile because GardenItem is not a | |
subclass of Watermelon; it's a superclass of Watermelon. | |
*/ | |
List(new GardenItem, food) // Fails! | |
} | |
case _ => List() | |
} | |
} | |
} |
references:
- Type Basics
- Covariance and Contravariance in Scala/