LoginSignup
0
2

More than 5 years have passed since last update.

[Java, Kotlin] Type Variance

Last updated at Posted at 2017-09-02

invariant

// Java
interface Collection<E> ... {
    void addAll(Collection<E> items);
}

void copyAll(Collection<Object> to, Collection<String> from) {
    to.addAll(from); // !!! Would not compile with the naive declaration of addAll
}

Collection<String> is not a subtype of Collection<Object>

covariant

  • lower to upper
  • producer

Java: Use-site variance; Kotlin: Type projections

// Java
interface Collection<E> ... {
    void addAll(Collection<? extends E> items);
}

void copyAll(Collection<Object> to, Collection<String> from) {
    to.addAll(from); // OK!
}

assign an instance of Collection<String> to Collection<? extends Object>
read E from Producer Collection<? extends E> items

// Kotlin
class Array<T>(val size: Int) {
    fun get(index: Int): T { /* ... */ }
    fun set(index: Int, value: T) { /* ... */ }
}

fun copy(from: Array<out Any>, to: Array<Any>) {
    assert(from.size == to.size)
    for (i in from.indices)
        to[i] = from[i]
}

val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3) { "" } 
copy(ints, any)

assign an instance of Array<Int> to Array<out Any>
from: Array<out Any> only with get function

Declaration-site variance

// Kotlin
abstract class Source<out T> {
    abstract fun nextT(): T
}

fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // This is OK, since T is an out-parameter
    // ...
}

assign an instance of Source<String> to Source<Any>
objects: Source<Any> Producer out Any

contravariant

  • upper to lower
  • consumer

Java: Use-site variance; Kotlin: Type projections

// Java
List<Animal> animals = new ArrayList<>();
List<? super Cat> cats = animals;
cats.add(new Cat());

assign an instance of List<Animal> to List<? super Cat>
write Cat to Consumer List<? super Cat> cats

// Kotlin
class Array<T>(val size: Int) {
    fun get(index: Int): T { /* ... */ }
    fun set(index: Int, value: T) { /* ... */ }
}

fun fill(dest: Array<in String>, value: String) {
    // ...
}

assign an instance of Array<CharSequence> or Array<Any> to Array<in String>
dest: Array<in String> only with set function

Declaration-site variance

// Kotlin
abstract class Comparable<in T> {
    abstract fun compareTo(other: T): Int
}

fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number
    // Thus, we can assign x to a variable of type Comparable<Double>
    val y: Comparable<Double> = x // OK!
}

assign an instance of Comparable<Number> to Comparable<Double>
y: Comparable<Double> Consumer in Double

Declaration-site to Use-site variance

interface Comparator<in T> {
    int compare(T o1, T o2);
}

interface Foo {
    Comparator<Integer> getComparator();
}

in T -> contravariant
upper Comparator<Integer> -> lower Comparator<? super Integer>

interface Comparator<T> {
    int compare(T o1, T o2);
}

interface Foo {
    Comparator<? super Integer> getComparator();
}

Use-site to Declaration-site variance

interface Baz<T, U> {
    Supplier<? extends U> baz(Consumer<? super T> consumer);
}

lower Consumer<? super T> -> upper Consumer<T>
covariant -> out T

upper Supplier<? extends U> -> lower Supplier<U>
contravariant -> in U

interface Baz<out T, in U> {
    Supplier<U> baz(Consumer<T> consumer);
}

Array

Java

covariant

Apple[] apples = new Apple[10];
Fruit[] fruits = apples;
fruits[0] = new Orange(); // throws an exception at runtime (ArrayStoreException or ArrayTypeMismatchException, respectively)
Apple apple = apples[0];

Kotlin

invariant
ensures compile time safety and prevents runtime errors

ref.

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2