in ve out ile Varyans
Coddy'nin Kotlin Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 40 / 57.
Bir List<String>, List<Any> beklenen yerde kullanılabilir, çünkü bir List yalnızca elemanları dışarı verir. Bir MutableList<String> ise kullanılamaz: onu MutableList<Any> olarak gören kod, dizelerden oluşan bir listeye bir Int ekleyebilir:
fun describeAll(items: List<Any>) = items.joinToString { it::class.simpleName ?: "?" }
fun addNumber(items: MutableList<Any>) { items.add(42) }main içinde:
val words = listOf("a", "b")
println(describeAll(words))
val mixed = mutableListOf<Any>("x")
addNumber(mixed)
println(mixed)
// addNumber(mutableListOf("a")) // hata: MutableList<String> bir MutableList<Any> değildirÇıktı:
String, String
[x, 42]out T bir genel sınıfı kovaryant yapar: yalnızca dönüş türleri olarak T değerleri üretir ve bunları hiçbir zaman girdi olarak almaz. Bu nedenle bir Producer<Cat>, Producer<Animal> olarak kabul edilir:
open class Animal(val name: String)
class Cat(name: String) : Animal(name)
class Producer<out T>(private val item: T) {
fun get(): T = item
}
fun greet(p: Producer<Animal>) = "hello ${p.get().name}"main içinde:
val cats: Producer<Cat> = Producer(Cat("Tom"))
println(greet(cats))Çıktı:
hello Tomin T onu kontravaryant yapar: yalnızca parametre olarak T değerlerini tüketir. Herhangi bir Animal türünü işleyen bir tüketici, Cat tüketicisinin yerine kullanılabilir:
open class Animal(val name: String)
class Cat(name: String) : Animal(name)
interface Feeder<in T> {
fun feed(animal: T): String
}
class AnimalFeeder : Feeder<Animal> {
override fun feed(animal: Animal) = "fed ${animal.name}"
}
fun feedCats(cats: List<Cat>, feeder: Feeder<Cat>) = cats.map { feeder.feed(it) }main içinde:
println(feedCats(listOf(Cat("Tom"), Cat("Kit")), AnimalFeeder()))Çıktı:
[fed Tom, fed Kit]Derleyici yönü zorunlu kılar: out tür parametresi bir metot parametresinin türü olamaz ve in tür parametresi döndürülemez. Pratik kural şudur: üretici out, tüketici in. Standart kütüphane de aynı yaklaşımı kullanır: List<out E>, Comparator<in T>. Bu anahtar sözcüklerden hiçbiri kullanılmadığında, tür parametresi hem okuyan hem de yazan MutableList<E> örneğinde olduğu gibi değişmezdir:
class Producer<out T>(private val item: T) {
fun get(): T = item // iyi: T dışarı çıkar
// fun set(x: T) { } // hata: T out olarak bildirilmiş
}
interface Feeder<in T> {
fun feed(animal: T): String // iyi: T içeri girer
// fun last(): T // hata: T in olarak bildirilmiş
}Varyans, bir türün kullanıldığı yerde de yazılabilir. MutableList<in Int>, Int değerlerini alabilen herhangi bir değiştirilebilir listeyi, örneğin bir MutableList<Number> listesini kabul eder; MutableList<out Number> ise yalnızca okumaya izin verir:
fun fillWith(target: MutableList<in Int>, n: Int) {
for (i in 1..n) target.add(i)
}
fun total(source: MutableList<out Number>) = source.sumOf { it.toDouble() }main içinde:
val numbers = mutableListOf<Number>(0.5)
fillWith(numbers, 3)
println(numbers)
println(total(mutableListOf(1, 2, 3)))Çıktı:
[0.5, 1, 2, 3]
6.0Görev
Kolayİki arayüz öğeleri hareket ettirir: Source<out T>, next() ile öğeler üretir (boş olduğunda null döndürür) ve Sink<in T>, bunları accept(item) ile tüketir. Bunlar sağlanmıştır. Listedeki öğeleri tek tek, ardından null döndüren ListSource<T>(items) sınıfını; kabul edilen öğeleri salt okunur bir items listesinde tutan Collector<T> sınıfını; ve kabul ettiği her şeyi sayan bir Sink<Any> olan Counter sınıfını yazın. Ardından kaynağın her öğesini her sink'e taşıyan ve kaç öğe taşıdığını döndüren pipe(from, vararg to) işlevini yazın.
Sağlanan kod, bir sözcük satırını bir Collector<String>'e ve bir sayı satırını bir Collector<Number>'a aktarır; her iki durumda da bunları ayrıca tek bir Counter'a aktarır. Bu, out ve in sayesinde çalışır: bir ListSource<Int>, bir Source<Number>'dır ve bir Sink<Any>, bir Sink<String>'dir. Sayaçları, her iki koleksiyonu, counted: 7 ve after the end: null ifadelerini yazdırır.
Kodunuz ListSource.kt, Collector.kt, Counter.kt, Pipe.kt, Source.kt ve Sink.kt dosyalarına gitmelidir. Main.kt, sağlanan girdi/çıktı kodunu içerir ve düzenlenemez.
Kendin dene
fun main() {
// Sağlanan giriş/çıkış kodu: olduğu gibi bırakın
val input = generateSequence(::readLine).toList()
val words = ListSource(input[0].split(" "))
val ints = ListSource(input[1].split(" ").map { it.toInt() })
val counter = Counter()
val allWords = Collector<String>()
val numbers = Collector<Number>()
println("words moved: " + pipe(words, allWords, counter))
println("numbers moved: " + pipe(ints, numbers, counter))
println(allWords.items)
println(numbers.items)
println("counted: ${counter.count}")
println("after the end: ${words.next()}")
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1Sınıflar ve Nesneler
Dosyalarla ÇalışmaSınıfları TanımlamaBirincil Yapıcılarinit Blokları ve Doğrulamaİkincil YapıcılarTekrar - Kütüphane Kartı4Kalıtım
Açık Sınıflar ve Alt SınıflarÜyeleri Geçersiz Kılmasuper ve YapıcılarSoyut SınıflarTekrar - Personel Dizini2Özellikler ve Kapsülleme
Özel ErişimcilerGörünürlük DeğiştiricileriÖzel Setter'larlazy ve lateinitTekrar - Banka Hesabı5Arayüzler
Arayüz BildirimleriVarsayılan MetotlarBirden Çok ArayüzTür Olarak ArayüzlerÖzet - Ödeme Yöntemleri8Generic'ler
Generic FonksiyonlarGeneric SınıflarTür Kısıtlamalarıin ve out ile VaryansTekrar - Öncelik Kuyruğu11Proje: Kütüphane Yönetimi
Kitaplar ve ÜyelerKitap Ödünç AlmaKendi başına pratik yap: Kotlin playground