Thomas Cook
Thomas Cook

Reputation: 4863

Why do I have to cast in this Kotlin code?

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

Answers (2)

Alexey Romanov
Alexey Romanov

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

IR42
IR42

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

Related Questions