Here a have a generic type, but I don't understand why even though I didn't made the type T covariant, I can add subclasses of Player to my generics class and function which has an upper bound T: Player; so why is the subtyping preserved without using the out keyword; and because of this I can - wrongly- add a BaseballPlayer and GamesPlayer to a football team. 1
class Team<T : Player>(val name: String, private val players: MutableList<T>) {
fun addPlayers(player: T) {
if (players.contains(player)) {
println("Player: ${(player as Player).name} is already in the team.")
} else {
players.add(player)
println("Player: {(player as Player).name} was added to the team.")
}
}
}
open class Player(open val name: String)
data class FootballPlayer(override val name: String) : Player(name)
data class BaseballPlayer(override val name: String) : Player(name)
data class GamesPlayer(override val name: String) : Player(name)
val footballTeam = Team<Player>(
"Football Team",
mutableListOf(FootballPlayer("Player 1"), FootballPlayer("Player 2"))
)
val baseballPlayer = BaseballPlayer("Baseball Player")
val footballPlayer = FootballPlayer("Football Player")
val gamesPlayer = GamesPlayer("Games Player")
footballTeam.addPlayers(baseballPlayer)
footballTeam.addPlayers(footballPlayer)
footballTeam.addPlayers(gamesPlayer)
CodePudding user response:
The mutable list you defined on this line:
mutableListOf(FootballPlayer("Player 1"), FootballPlayer("Player 2"))
is not a MutableList<FootballPlayer>
. It is a MutableList<Player>
, since you didn't specify its type, so the compiler used type inference to assume you want a MutableList<Player>
to fit the constructor argument for your Team<Player>
constructor.
So, it is valid to put any kind of Player
into a MutableList<Player>
, since it can only ever return items of type Player
. It's still type safe.
If you had been explicit about the type, it would have been a compile error:
val footballTeam = Team<Player>(
"Football Team",
mutableListOf<FootballPlayer>(FootballPlayer("Player 1"), FootballPlayer("Player 2"))
//error, expected MutableList<Player>
)
Or if you had omitted the type from the Team constructor, it would have assumed you wanted a Team<FootballPlayer>
and you would have had an error when trying to add other types of player:
val footballTeam = Team(
"Football Team",
mutableListOf(FootballPlayer("Player 1"), FootballPlayer("Player 2"))
)
// ^^ is a Team<FootballPlayer> because of type inferrence.
footballTeam.addPlayers(BaseballPlayer("Foo")) // error, expected FootballPlayer