Varians dengan in dan out
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Kotlin di Coddy. Pelajaran 40 dari 57.
List<String> dapat diberikan ketika yang diharapkan adalah List<Any>, karena List hanya mengeluarkan elemen. MutableList<String> tidak bisa: kode yang melihatnya sebagai MutableList<Any> dapat menambahkan Int ke list string:
fun describeAll(items: List<Any>) = items.joinToString { it::class.simpleName ?: "?" }
fun addNumber(items: MutableList<Any>) { items.add(42) }Di dalam main:
val words = listOf("a", "b")
println(describeAll(words))
val mixed = mutableListOf<Any>("x")
addNumber(mixed)
println(mixed)
// addNumber(mutableListOf("a")) // error: MutableList<String> bukan MutableList<Any>Output:
String, String
[x, 42]out T membuat generic class kovarian: class ini hanya menghasilkan nilai T sebagai tipe kembalian dan tidak pernah menerimanya. Jadi, Producer<Cat> dianggap sebagai Producer<Animal>:
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}"Di dalam main:
val cats: Producer<Cat> = Producer(Cat("Tom"))
println(greet(cats))Output:
hello Tomin T menjadikannya kontravarian: hanya menerima nilai T sebagai parameter. Konsumen yang menangani semua Animal dapat digunakan sebagai pengganti konsumen Cat:
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) }Di dalam main:
println(feedCats(listOf(Cat("Tom"), Cat("Kit")), AnimalFeeder()))Output:
[fed Tom, fed Kit]Kompilator menegakkan arah tersebut: parameter tipe out tidak dapat menjadi tipe parameter method, dan parameter tipe in tidak dapat dikembalikan. Aturan praktisnya adalah produsen out, konsumen in. Pustaka standar menerapkan hal yang sama: List<out E>, Comparator<in T>. Tanpa salah satu kata kunci tersebut, parameter tipe bersifat invarian, seperti pada MutableList<E>, yang dapat membaca dan menulis:
class Producer<out T>(private val item: T) {
fun get(): T = item // benar: T keluar
// fun set(x: T) { } // error: T dideklarasikan sebagai out
}
interface Feeder<in T> {
fun feed(animal: T): String // benar: T masuk
// fun last(): T // error: T dideklarasikan sebagai in
}Variance juga dapat ditulis di tempat tipe digunakan. MutableList<in Int> menerima mutable list apa pun yang dapat menampung nilai Int, seperti MutableList<Number>, sedangkan MutableList<out Number> hanya memungkinkan pembacaan:
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() }Di dalam main:
val numbers = mutableListOf<Number>(0.5)
fillWith(numbers, 3)
println(numbers)
println(total(mutableListOf(1, 2, 3)))Output:
[0.5, 1, 2, 3]
6.0Tantangan
MudahDua interface memindahkan item: Source<out T> menghasilkan item dengan next() (mengembalikan null saat kosong), sedangkan Sink<in T> mengonsumsinya dengan accept(item). Keduanya sudah disediakan. Tulis ListSource<T>(items), yang mengembalikan item-item dari list satu per satu lalu null; Collector<T>, yang menyimpan item yang diterima dalam list items hanya-baca; dan Counter, yaitu Sink<Any> yang menghitung semua item yang diterimanya. Lalu tulis pipe(from, vararg to), yang memindahkan setiap item dari source ke setiap sink dan mengembalikan jumlah item yang dipindahkannya.
Kode yang disediakan menyalurkan satu baris kata ke Collector<String> dan satu baris angka ke Collector<Number>, keduanya juga disalurkan ke satu Counter. Ini berfungsi karena out dan in: ListSource<Int> adalah Source<Number>, dan Sink<Any> adalah Sink<String>. Kode ini mencetak jumlah hitungan, kedua koleksi, counted: 7 dan after the end: null.
Kodemu ditulis dalam ListSource.kt, Collector.kt, Counter.kt, Pipe.kt, Source.kt dan Sink.kt. Main.kt berisi kode input/output yang disediakan dan tidak boleh diedit.
Coba sendiri
fun main() {
// Kode input/output yang disediakan: biarkan apa adanya
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()}")
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1class dan object
Bekerja dengan fileMendefinisikan classKonstruktor utamaBlok init dan validasiKonstruktor sekunderRingkasan: Kartu Perpustakaan2property dan Enkapsulasi
Accessor KustomModifier VisibilitasSetter Privatlazy dan lateinitRingkasan: Rekening Bank5Interface
Mendeklarasikan Interfacemethod DefaultBeberapa InterfaceInterface sebagai TipeUlasan: Metode Pembayaran11Proyek: Pengelolaan Perpustakaan
Buku dan AnggotaPeminjaman BukuLatihan sendiri: Playground Kotlin