Программирование на Kotlin, осень 2016: Variance
Transcript of Программирование на Kotlin, осень 2016: Variance
![Page 1: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/1.jpg)
Variance
Svetlana Isakova
![Page 2: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/2.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/3.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/4.jpg)
java.lang.ArrayStoreException
String[] strings = new String[] { "a", "b", "c" };foo(strings);
private static void foo(Object[] array) {
/* ... */
}
![Page 5: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/5.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/6.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/7.jpg)
Definitions
![Page 8: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/8.jpg)
Classes & types
Classes Types
String String String?
ListList<Int>
List<String?> List<List<String>>
![Page 9: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/9.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/10.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/11.jpg)
Subtyping for nullable typesval s: String = "abc"val t: String? = s
A
A?
Int?
Int
Int
Int?
✗
![Page 12: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/12.jpg)
Subtyping for generic types
Foo<B>
Foo<A>
B
A
?
interface Foo<T> {}
![Page 13: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/13.jpg)
Declaration-site variance
![Page 14: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/14.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/15.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/16.jpg)
List & MutableList
MutableList<String>
MutableList<Any>
String
Any
✗List<String>
List<Any>
![Page 17: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/17.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/18.jpg)
Kotlin: read-only lists
val list = listOf("a", "b", "c") foo(list)
fun foo(list: List<Any>) { list += 42 }
![Page 19: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/19.jpg)
Declaration-site variance
![Page 20: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/20.jpg)
in / out positions
interface Transformer<T, R> { fun transform(t: T): R }
“in” position “out” position
![Page 21: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/21.jpg)
<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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/22.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/23.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/24.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/25.jpg)
@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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/26.jpg)
Contravariance
![Page 27: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/27.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/28.jpg)
<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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/29.jpg)
Example: comparator
Comparator<Int>
Comparator<Any>
Int
Any
interface Comparator<in T> { fun compare(o1: T, o2: T): Int}
![Page 30: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/30.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/31.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/32.jpg)
(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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/33.jpg)
Use-site variance
![Page 34: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/34.jpg)
Java wildcards
public interface Stream<T> { <R> Stream<R> map( Function<? super T, ? extends R> mapper);}
![Page 35: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/35.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/36.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/37.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/38.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/39.jpg)
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](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/40.jpg)
Star projection
MutableList<*> ≠ MutableList<Any?>
List<*> = List<out Any?>
List<Any?>
=
![Page 41: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/41.jpg)
Using List<*>
fun printFirst(list: List<*>) { if (list.isNotEmpty()) { println(list.first()) }}
returns Any?
![Page 42: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/42.jpg)
Star projection
MutableList<*> ≠ MutableList<Any?>
MutableList<*> = MutableList<out Any?>
Comparator<*> = Comparator<in ???>Comparator<in Nothing>
![Page 43: Программирование на Kotlin, осень 2016: Variance](https://reader034.fdocuments.us/reader034/viewer/2022051123/58749c8a1a28abfc5f8b6451/html5/thumbnails/43.jpg)
Thank you!