43
Variance Svetlana Isakova

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

Embed Size (px)

Citation preview

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

Variance

Svetlana Isakova

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

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

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

Example: Java arrays

• yes

String[]

Object[]

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

• Is it safe?

• no

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

java.lang.ArrayStoreException

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

private static void foo(Object[] array) {

/* ... */

}

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

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

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

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>

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

Definitions

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

Classes & types

Classes Types

String String String?

ListList<Int>

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

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

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

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

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

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

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

A

A?

Int?

Int

Int

Int?

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

Subtyping for generic types

Foo<B>

Foo<A>

B

A

?

interface Foo<T> {}

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

Declaration-site variance

Page 14: Программирование на Kotlin, осень 2016: 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

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

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

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

List & MutableList

MutableList<String>

MutableList<Any>

String

Any

✗List<String>

List<Any>

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

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

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

Kotlin: read-only lists

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

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

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

Declaration-site variance

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

in / out positions

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

“in” position “out” position

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

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

T must be used only in “out” positions

Producer<Int>

Producer<Any>

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

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 }

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

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

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

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}

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

@UnsafeVariance

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

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

Contravariance

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

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

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

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

T must be used only in “in” positions

Consumer<Int>

Consumer<Any>

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

Example: comparator

Comparator<Int>

Comparator<Any>

Int

Any

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

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

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

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

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

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>✗

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

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

(Animal) -> Int

(Cat) -> Number

Animal

Cat

Int

Number

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

Use-site variance

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

Java wildcards

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

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

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

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

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) ✓

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

Another solution: MutableList<out T>

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

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

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'

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

MutableList<in T>

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

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

Star projection

MutableList<*> ≠ MutableList<Any?>

List<*> = List<out Any?>

List<Any?>

=

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

Using List<*>

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

returns Any?

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

Star projection

MutableList<*> ≠ MutableList<Any?>

MutableList<*> = MutableList<out Any?>

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

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

Thank you!