Программирование на Kotlin, осень 2016: Variance

Post on 15-Apr-2017

140 views 6 download

Transcript of Программирование на Kotlin, осень 2016: Variance

Variance

Svetlana Isakova

Producer<Cat>

Producer<Animal>

Cat

Animal

Consumer<Cat>

Consumer<Animal>

VarianceDescribes how types with the same base type and different type arguments relate to each other

Example: Java arrays

• yes

String[]

Object[]

• Is it possible in Java to pass String[] as an argument for Object[] parameter?

• Is it safe?

• no

java.lang.ArrayStoreException

String[] strings = new String[] { "a", "b", "c" };foo(strings);

private static void foo(Object[] array) {

/* ... */

}

java.lang.ArrayStoreException

String[] strings = new String[] { "a", "b", "c" };foo(strings);

private static void foo(Object[] array) { array[1] = 42; } java.lang.ArrayStoreException: java.lang.Integer

Java: lists are safeList<String> strings = Arrays.asList("a", "b", "c");foo(strings);

private static void foo(List<Object> list) { list.set(0, 42); }

Error: incompatible types: java.util.List<java.lang.String> cannot be converted to java.util.List<java.lang.Object>

Definitions

Classes & types

Classes Types

String String String?

ListList<Int>

List<String?> List<List<String>>

Subtyping• A type B is a subtype of a type A if you can use

the value of the type B whenever a value of the type A is required.

Int

Number

B

A

Int

String

Int

Int

The compiler always checks subtyping

fun test(i: Int) { val n: Number = i fun f(s: String) { /*...*/ } f(i)} Error: inferred type is Int but String was expected

Subtyping for nullable typesval s: String = "abc"val t: String? = s

A

A?

Int?

Int

Int

Int?

Subtyping for generic types

Foo<B>

Foo<A>

B

A

?

interface Foo<T> {}

Declaration-site variance

Invariant classesA generic class Foo is called invariant on the type parameter if, for any two different types A and B, Foo<A> is not a subtype or a supertype of Foo<B>

Foo<A>

Foo<B>

✗Foo<B>

Foo<A>

✗B

A

CovarianceA generic class Producer<T> is covariant on its type parameter if the following holds:

Producer<B> is a subtype of Producer<A> when B is a subtype of A.

Producer<B>

Producer<A>

B

A

List & MutableList

MutableList<String>

MutableList<Any>

String

Any

✗List<String>

List<Any>

Kotlin: mutable lists

val list = mutableListOf("a", "b", "c") foo(list)

fun foo(list: MutableList<Any>) { list += 42 }

Error: Type mismatch: inferred type is MutableList<String> but MutableList<Any> was expected

Kotlin: read-only lists

val list = listOf("a", "b", "c") foo(list)

fun foo(list: List<Any>) { list += 42 }

Declaration-site variance

in / out positions

interface Transformer<T, R> { fun transform(t: T): R }

“in” position “out” position

<out T> means covariantinterface Producer<out T> { fun produce(): T }

T must be used only in “out” positions

Producer<Int>

Producer<Any>

Why is it safe?

val producer: Producer<Int>foo(producer)

fun foo(anyProducer: Producer<Any>) { val any = anyProducer.produce()}

interface Producer<out T> { fun produce(): T }

interface Foo<T> { fun bar(): T fun baz(t: T) }

Foo can’t be declared as covariant, because T is used both in “out” and “in” positions

<T> means invariant

List<out T>interface List<out T> : Collection<T> { operator fun get(index: Int): T fun subList(fromIndex: Int, toIndex: Int): List<T> // ...}

interface MutableList<T> : List<T>, MutableCollection<T> { override fun add(element: T): Boolean}

@UnsafeVariance

interface List<out T> : Collection<T> { operator fun get(index: Int): T override fun contains( element: @UnsafeVariance T): Boolean // ...}

Contravariance

ContravarianceA generic class Consumer<T> is contravariant on its type parameter if the following holds:

Consumer<A> is a subtype of Consumer<B> when B is a subtype of A.

Consumer<B>

Consumer<A>

B

A

<in T> means contravariantinterface Consumer<in T> { fun consume(t: T) }

T must be used only in “in” positions

Consumer<Int>

Consumer<Any>

Example: comparator

Comparator<Int>

Comparator<Any>

Int

Any

interface Comparator<in T> { fun compare(o1: T, o2: T): Int}

Using comparatorval anyComparator: Comparator<Any> = Comparator { a, b -> a.hashCode() - b.hashCode() }

fun foo(strComparator: Comparator<String>) { listOf("a", "c", “b"). sortedWith(strComparator)}

Covariant, contravariant & invariant classes

Covariant Contravariant Invariant

Producer<out T> Consumer<in T> Foo<T>

T only in "out" positions

T only in "in" positions T in any position

Producer<Int>

Producer<Any> Consumer<Any>

Consumer<Int>

Foo<Any>

Foo<Int>✗

(T) -> Rinterface Function1<in P, out R> { operator fun invoke(p: P): R }

(Animal) -> Int

(Cat) -> Number

Animal

Cat

Int

Number

Use-site variance

Java wildcards

public interface Stream<T> { <R> Stream<R> map( Function<? super T, ? extends R> mapper);}

copyData examplefun <T> copyData( source: MutableList<T>, destination: MutableList<T>) { for (item in source) { destination.add(item) }}

val ints = mutableListOf(1, 2, 3) val anyItems = mutableListOf<Any>()copyData(ints, anyItems)

Error: Cannot infer type parameter T

First solution: one more type parameter

fun <T : R, R> copyData( source: MutableList<T>, destination: MutableList<R>) { for (item in source) { destination.add(item) }}

val ints = mutableListOf(1, 2, 3) val anyItems = mutableListOf<Any>()copyData(ints, anyItems) ✓

Another solution: MutableList<out T>

fun <T> copyData( source: MutableList<out T>, destination: MutableList<T>) { for (item in source) { destination.add(item) }} ✓

Projected type

val list: MutableList<out Number> = … list.add(42)

Error: Out-projected type 'MutableList<out Number>' prohibitsthe use of 'fun add(element: E): Boolean'

MutableList<in T>

fun <T> copyData( source: MutableList<T>, destination: MutableList<in T>) { for (item in source) { destination.add(item) }} ✓

Star projection

MutableList<*> ≠ MutableList<Any?>

List<*> = List<out Any?>

List<Any?>

=

Using List<*>

fun printFirst(list: List<*>) { if (list.isNotEmpty()) { println(list.first()) }}

returns Any?

Star projection

MutableList<*> ≠ MutableList<Any?>

MutableList<*> = MutableList<out Any?>

Comparator<*> = Comparator<in ???>Comparator<in Nothing>

Thank you!