Variansi di Lokasi Deklarasi: in dan out
Gunakan parameter tipe kovarian (out) dan kontravarian (in) dengan benar.
Variansi di Lokasi Deklarasi: in dan out adalah pelajaran Kotlin Academy gratis di CoddyKit. Ini adalah pelajaran 2 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Kotlin Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Kotlin Academy mencakup 4 pelajaran total.
Masalah Variansi
Di Kotlin, List<String> adalah subtipe dari List<Any> karena List dideklarasikan dengan out T. Tanpa anotasi variansi, hubungan ini tidak akan berlaku.
val strings: List<String> = listOf("a", "b")
val anys: List<Any> = strings // OK because List<out T>
// MutableList<String> is NOT a subtype of MutableList<Any>:
// val m: MutableList<Any> = mutableListOf("x") // compile error
fun main() { println(anys) }Kovariansi dengan out
out T berarti kelas hanya dapat menghasilkan nilai T (mengembalikannya), bukan mengonsumsinya. Hal ini menjadikan Producer<Dog> sebagai subtipe dari Producer<Animal>.
interface Producer<out T> {
fun produce(): T
}
class DogProducer : Producer<String> {
override fun produce() = "Woof!"
}
fun sound(p: Producer<Any>) = println(p.produce())
fun main() {
val dog = DogProducer()
sound(dog) // OK: Producer<String> is a subtype of Producer<Any>
}Kontravariansi dengan in
in T berarti kelas hanya dapat mengonsumsi nilai T (menerimanya sebagai parameter), bukan menghasilkannya. Hal ini menjadikan Consumer<Animal> sebagai subtipe dari Consumer<Dog>.
interface Consumer<in T> {
fun consume(item: T)
}
class Printer : Consumer<Any> {
override fun consume(item: Any) = println(item)
}
fun feedDog(consumer: Consumer<String>) = consumer.consume("Dog")
fun main() {
val printer = Printer()
feedDog(printer) // OK: Consumer<Any> is a subtype of Consumer<String>
}Batasan out: Tidak Ada Posisi in
Dengan out T, T tidak boleh muncul pada posisi parameter (in). Kompilator memberlakukan aturan ini.
interface ReadOnly<out T> {
fun get(): T // OK: out position
// fun set(t: T) {} // Error: T in in-position violates out variance
}
class Box<out T>(private val value: T) : ReadOnly<T> {
override fun get() = value
}
fun main() {
val box: ReadOnly<Any> = Box<String>("hello")
println(box.get())
}Batasan in: Tidak Ada Posisi out
Dengan in T, T tidak boleh muncul pada posisi hasil (out).
interface WriteOnly<in T> {
fun set(t: T) // OK: in position
// fun get(): T {} // Error: T in out-position violates in variance
}
class Sink<in T> : WriteOnly<T> {
override fun set(t: T) = println("Received: $t")
}
fun main() {
val sink: WriteOnly<String> = Sink<Any>()
sink.set("hello")
}Contoh Pustaka Standar Kotlin
Comparable<in T> bersifat kontravarian: Comparable<Number> dapat membandingkan subtipe Number apa pun. List<out T> bersifat kovarian: daftar kucing merupakan daftar hewan.
fun sortNumbers(list: List<Number>) = list.sortedWith(compareBy { it.toDouble() })
fun main() {
val ints: List<Int> = listOf(3, 1, 2)
val sorted = sortNumbers(ints) // OK: List<Int> is List<Number>
println(sorted)
}Invariansi: MutableList
MutableList<T> bersifat invarian: tipe ini dapat menghasilkan dan mengonsumsi T, sehingga tidak ada hubungan subtipe antara tipe T yang berbeda.
fun addNumber(list: MutableList<Number>) { list.add(1.5) }
fun main() {
val ints = mutableListOf<Int>(1, 2)
// addNumber(ints) // compile error: MutableList<Int> is not MutableList<Number>
val nums = mutableListOf<Number>(1, 2)
addNumber(nums) // OK
println(nums)
}Variansi di Lokasi Pemakaian sebagai Alternatif
Saat Anda tidak dapat mengubah kelasnya, gunakan variansi di lokasi pemakaian: gunakan out di lokasi pemanggilan untuk memproyeksikan tipe menjadi kovarian.
fun copy(from: MutableList<out Any>, to: MutableList<Any>) {
for (item in from) to.add(item)
}
fun main() {
val src = mutableListOf("a", "b", "c")
val dest = mutableListOf<Any>()
copy(src, dest)
println(dest)
}Praktik: Pola Repository
Gunakan kovariansi untuk repositori hanya-baca dan kontravariansi untuk saluran hanya-tulis guna memodelkan aliran data yang bersih.
interface Repository<out T> {
fun findAll(): List<T>
fun findById(id: Int): T?
}
interface Writer<in T> {
fun save(entity: T)
}
interface ReadWriteRepo<T> : Repository<T>, Writer<T>
// ReadWriteRepo<User> is neither sub nor super of ReadWriteRepo<Admin>Panduan Menentukan Variansi
Pilih variansi berdasarkan cara T digunakan: out = produsen (hanya mengembalikan T), in = konsumen (hanya menerima T), invarian = keduanya (MutableList, Channel).
// Quick mental model:
// out T: source/producer — List, Flow, Sequence
// in T: sink/consumer — Comparable, Continuation
// invariant: read+write — MutableList, Channel, MutableStateFlow
fun main() {
val nums: List<Number> = listOf(1, 2, 3) // List is out
println(nums)
}Kelas Invarian dengan Fungsi out
Bahkan dalam kelas invarian, Anda dapat menggunakan out di lokasi pemakaian untuk parameter fungsi tertentu.
class Stack<T>(private val items: MutableList<T> = mutableListOf()) {
fun push(item: T) = items.add(item)
fun pop(): T? = items.removeLastOrNull()
}
fun printAll(stack: Stack<out Any>) {
// can only read, not write
println(stack.pop())
}
fun main() {
val s = Stack<String>()
s.push("hello")
printAll(s)
}Pemeriksaan Singkat
Apa arti out T pada parameter tipe?
Rekapitulasi
out T (kovariansi) memungkinkan hubungan subtipe saat nilai dihasilkan. in T (kontravariansi) memungkinkan hubungan subtipe saat nilai dikonsumsi. Tipe invarian (dibaca dan ditulis) tidak memiliki hubungan subtipe untuk T yang berbeda.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Variansi di Lokasi Deklarasi: in dan out” gratis?
Ya — teks lengkap “Variansi di Lokasi Deklarasi: in dan out” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Kotlin Academy, upgrade ke CoddyKit PRO. Kursus Kotlin Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Variansi di Lokasi Deklarasi: in dan out”?
Gunakan parameter tipe kovarian (out) dan kontravarian (in) dengan benar. Kamu berlatih Kotlin Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Kotlin Academy?
Tidak diperlukan pengalaman sebelumnya. Kotlin Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 2 dari 4.
Berapa lama pelajaran “Variansi di Lokasi Deklarasi: in dan out” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Kotlin Academy ini?
Ya. Setiap pelajaran Kotlin Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Fungsi Generik dan Batasan Tipe dengan where
- Variansi di Lokasi Deklarasi: in dan out
- Proyeksi Bintang dan Kapan Menggunakan *
- Penghapusan Tipe dan Parameter Tipe reified