Reputation: 4863
interface Foo<T: Bar> {
fun example(bar: T)
}
interface Bar
class Bar1 : Bar
class Bar2 : Bar
class FooEx1 : Foo<Bar1> {
override fun example(bar: Bar1) { }
}
class FooEx2 : Foo<Bar2> {
override fun example(bar: Bar2) { }
}
// Won't compile
// Even though FooEx1 and FooEx2 *are* Foo<Bar>
class ExampleDoesntCompile {
val collection = mutableListOf<Foo<Bar>>().apply {
this.add(FooEx1())
this.add(FooEx2())
}
}
// Will compile
// But have to cast FooEx1 and FooEx2 to Foo<Bar>
class ExampleDoesCompileButRequiresCast {
val collection = mutableListOf<Foo<Bar>>().apply {
this.add(FooEx1() as Foo<Bar>)
this.add(FooEx2() as Foo<Bar>)
}
}
So, I could for instance, state that Foo's parameterized type is out
, but then I get a compile error for the function example
:
interface Foo<out T: Bar> {
fun example(bar: T)
}
Error: Type parameter T is declared as 'out' but occurs in 'in' position in type T
Upvotes: 1
Views: 222
Reputation: 170909
So it'll crash at run time with the cast?
Here is example code that would crash:
val foo: Foo<Bar> = collection[0]
foo.example(Bar2())
So if you could create collection
without a cast as in your ExampleDoesntCompile
, you'd get code without any casts which throws a ClassCastExcepion
.
This also shows why the interface can't be declared with out
:
val foo: Foo<Bar> = FooEx1() // out would make this legal
foo.example(Bar2())
It would make sense to declare your interface with in
, but this would mean a Foo<Bar>
is a Foo<Bar1>
and a Foo<Bar2>
, not vice versa, so still wouldn't let you put FooEx1
/2
into a collection of Foo<Bar>
s.
Upvotes: 0
Reputation: 9732
Because generic types in Java / Kotlin are invariant by default. variance
interface Foo<out T: Bar>
If you can't make it covariant, then make the list items covariant
val collection = mutableListOf<Foo<out Bar>>().apply {
this.add(FooEx1())
this.add(FooEx2())
}
//or val collection = mutableListOf(FooEx1(), FooEx2())
Upvotes: 2