Reputation: 4322
Ok, so I have three interface types.
Movement<T : Animal>
Animal
with subinterfaces Cat
, Dog
, Horse
AnimalMovement
Movement Interface
interface Movement<T : Animal> {
fun moveAnimal(type:T)
}
Animal Interfaces
interface Animal {
fun takeSteps()
fun jump()
fun hide()
}
interface Cat : Animal
interface Dog : Animal
AnimalMovement
interface CatMovement : Movement<Cat>
I then implement the CatMovement interface
class CatMovementImpl : CatMovement {
override fun moveAnimal(type: Cat) {
TODO("not implemented")
}
}
Problem
fun TestGenerics() {
var catMovement : Movement<Cat> = CatMovementImpl() // this works
var catMovement : Movement<Animal> = CatMovementImpl() // this doesn't?
}
I am sure in Java both lines would have worked fine. However in Kotlin the second line fails to execute. Why would that be? An animal is the base type for cat, so this should have worked right?
Upvotes: 0
Views: 59
Reputation: 29884
What moskito said in the comments is correct.
I am pretty sure that doesn't work in Java either. A
Movement<Cat>
is NOT a subtype ofMovement<Animal>
, the same way a List is NOT a subtype ofList<Object>
. You might want to read this.
But in Kotlin this is possible using type variance.
fun TestGenerics() {
var catMovement1: Movement<Cat> = CatMovementImpl()
var catMovement2: Movement<out Animal> = CatMovementImpl() // works
}
You basically tell the compiler "accept all implementations of Movement<Animal>
or implementations Movement<S>
for which S
has Animal
as upper bound".
But, then a problem arises. You cannot invoke
val cat: Cat = /* ... */
catMovement2.moveAnimal(cat) // error
giving you the error
Out-projected type
Movement<out Animal>
prohibits the use of [...].
because T
can only be used as producer (out position) and not as consumer (in position) like this (function made up to demonstrate the point):
val c: Cat = catMovement2.getAnimal() // works
This problem becomes clear right away when you use out
at the Movement
declaration like this:
interface Movement<out T : Animal> {
fun moveAnimal(type: T) // error
}
It depends on your use case but maybe you should just let Kotlin infer the type, which would be CatMovementImpl
.
var catMovement = CatMovementImpl()
Credit goes to EpicPandaForce for already suggesting using out
in the comments.
Upvotes: 1
Reputation: 5394
I am not an expert of Kotlin, but that seems perfectly normal:
When declared like this:
var animalMovement : Movement<Animal>
You can write code:
animalMovement.moveAnimal(dog)
But if assigning it like this:
var animalMovement : Movement<Animal> = CatMovementImpl()
is allowed, it means that your CatMovementImpl
should be able to move a dog ?
Upvotes: 2