Varians: Kovarians & Kontravarians
Kuasai kovarians dan kontravarians untuk menangani hubungan subtipe dalam tipe generik dengan benar.
Varians: Kovarians & Kontravarians adalah pelajaran Scala for Backend Engineering & Functional Programming gratis di CoddyKit. Ini adalah pelajaran 2 dari 3. 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 Scala for Backend Engineering & Functional Programming, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Scala for Backend Engineering & Functional Programming mencakup 3 pelajaran total.
Apa Itu Variansi Tipe?
Di Scala, ketika Anda memiliki hierarki tipe (misalnya, Cat adalah subtipe dari Animal), bagaimana perilaku tipe generik?
Apakah List[Cat] dianggap sebagai subtipe dari List[Animal]? Secara bawaan, belum tentu!
Variansi tipe memungkinkan kita menentukan bagaimana hubungan subtipe dipertahankan atau dibalik untuk tipe generik. Hal ini penting untuk menulis kode yang fleksibel dan aman terhadap tipe.
Kovariansi: Tipe 'Producer' (+T)
Kovariansi ditandai dengan menempatkan + sebelum parameter tipe (misalnya, trait Box[+T]).
- Jika
Aadalah subtipe dariB, makaBox[A]menjadi subtipe dariBox[B]. - Anggaplah tipe kovarian sebagai 'penghasil'
T. Tipe tersebut hanya dapat muncul pada posisi keluaran, seperti tipe kembalian metode. - Artinya, jika Anda mengharapkan
Box[Animal], Anda dapat memberikanBox[Cat]karena tipe tersebut 'menghasilkan' sesuatu yang lebih spesifik (yaituCat, yang merupakanAnimal).
Kovariansi dalam Praktik
Cobalah menjalankan contoh Producer yang bersifat kovarian berikut:
class Animal
class Cat extends Animal
trait Producer[+T] {
def produce: T // Output position
}
class CatProducer extends Producer[Cat] {
def produce: Cat = new Cat
}
object Main {
def main(args: Array[String]): Unit = {
val catProducer: Producer[Cat] = new CatProducer
// Because Producer is covariant, Producer[Cat] is a subtype of Producer[Animal]
val animalProducer: Producer[Animal] = catProducer
println("Assigned CatProducer to AnimalProducer.")
println(s"Produced: ${animalProducer.produce.getClass.getSimpleName}")
}
}Kapan Menggunakan Kovariansi
Kovariansi aman digunakan ketika tipe generik Anda hanya 'menghasilkan' nilai bertipe T, atau tidak pernah menerima T sebagai argumen.
- Koleksi tidak dapat diubah seperti
List[+T]adalah contoh utamanya. Anda dapat memperlakukanList[Cat]sebagaiList[Animal]karena Anda hanya 'membaca'Animalatau subtipenya dari dalam koleksi tersebut. - Anda tidak dapat menambahkan
Animalsembarang keList[Cat]jika koleksi itu diberi tipeList[Animal]. Hal ini menjaga keamanan tipe.
Kontravariansi: Tipe 'Consumer' (-T)
Kontravariansi ditandai dengan menempatkan - sebelum parameter tipe (misalnya, trait Consumer[-T]).
- Jika
Aadalah subtipe dariB, makaConsumer[B]menjadi subtipe dariConsumer[A]. - Anggaplah tipe kontravarian sebagai 'pengonsumsi'
T. Tipe tersebut hanya dapat muncul pada posisi masukan, seperti tipe parameter metode. - Artinya, jika Anda mengharapkan
Consumer[Cat], Anda dapat memberikanConsumer[Animal]karena tipe tersebut 'mengonsumsi' sesuatu yang lebih umum dan dapat menanganiAnimalapa pun, termasukCat.
Kontravariansi dalam Praktik
Cobalah menjalankan contoh Consumer yang bersifat kontravarian berikut:
class Animal
class Cat extends Animal
trait Consumer[-T] {
def consume(item: T): Unit // Input position
}
class AnimalConsumer extends Consumer[Animal] {
def consume(item: Animal): Unit = {
println(s"Consumed an animal: ${item.getClass.getSimpleName}")
}
}
object Main {
def main(args: Array[String]): Unit = {
val animalConsumer: Consumer[Animal] = new AnimalConsumer
// Because Consumer is contravariant, Consumer[Animal] is a subtype of Consumer[Cat]
val catConsumer: Consumer[Cat] = animalConsumer
catConsumer.consume(new Cat)
println("Assigned AnimalConsumer to CatConsumer.")
}
}Kapan Menggunakan Kontravariansi
Kontravariansi aman digunakan ketika tipe generik Anda hanya 'mengonsumsi' nilai bertipe T, atau tidak pernah mengembalikan T.
- Contoh umumnya adalah fungsi, khususnya tipe parameter masukan. Jika suatu fungsi dapat memproses
Animalapa pun (Animal => Unit), fungsi tersebut tentu dapat memprosesCat. Jadi,(Animal => Unit)adalah subtipe dari(Cat => Unit). - Hal ini memberikan fleksibilitas yang lebih besar saat meneruskan fungsi sebagai argumen.
Invariansi: Perilaku Bawaan
Jika Anda tidak menentukan + atau -, parameter tipe tersebut bersifat invarian. Inilah perilaku bawaan di Scala.
Box[A]hanya menjadi subtipe dariBox[B]jikaAdanBmerupakan tipe yang persis sama.- Hal ini sering diperlukan untuk koleksi yang dapat diubah (misalnya,
Array[T]) guna mencegah masalah keamanan tipe, karena Anda dapat membaca dan menulis subtipe yang berbeda.
class Food
class Apple extends Food
// Invariant Box
class Box[T](val item: T) {
def getContent: T = item
}
object Main {
def main(args: Array[String]): Unit = {
val appleBox = new Box(new Apple)
// The following line would cause a compile error:
// val foodBox: Box[Food] = appleBox
println(s"An Apple Box contains: ${appleBox.getContent.getClass.getSimpleName}")
println("Box[Apple] is NOT a subtype of Box[Food] (invariant).")
println("The types must match exactly for invariant types.")
}
}Fungsi: Kovarian dan Kontravarian Sekaligus
Tipe fungsi Scala, Function1[-A, +B], menggabungkan kedua jenis variansi dengan baik:
- Parameter masukan
Abersifat kontravarian (-A). Artinya, fungsi yang menerima tipe lebih umum (misalnya,Animal) dapat digunakan di tempat yang mengharapkan fungsi yang menerima tipe lebih spesifik (misalnya,Cat). - Tipe kembalian
Bbersifat kovarian (+B). Artinya, fungsi yang mengembalikan tipe lebih spesifik (misalnya,Cat) dapat digunakan di tempat yang mengharapkan fungsi yang mengembalikan tipe lebih umum (misalnya,Animal).
class Vehicle
class Car extends Vehicle
object Main {
def main(args: Array[String]): Unit = {
// Contravariance for input: (Vehicle => Unit) is a subtype of (Car => Unit)
val printVehicle: Vehicle => Unit = (v: Vehicle) => println(s"Printing vehicle: ${v.getClass.getSimpleName}")
val printCar: Car => Unit = printVehicle // OK: A general printer can print a specific car
printCar(new Car)
// Covariance for output: (() => Car) is a subtype of (() => Vehicle)
val getCar: () => Car = () => new Car
val getVehicle: () => Vehicle = getCar // OK: A specific producer can fulfill a general request
println(s"Got vehicle: ${getVehicle().getClass.getSimpleName}")
}
}Pemeriksaan Singkat: Aturan Variansi
Perhatikan trait berikut:
trait Handler[T] {
def handle(item: T): Unit
}Agar Handler[Animal] dapat digunakan di tempat yang mengharapkan Handler[Cat] (dengan Cat yang merupakan turunan dari Animal), anotasi variansi apa yang harus dimiliki T?
Rangkuman: Menguasai Variansi
Anda telah menguasai variansi di Scala! Berikut rangkuman singkatnya:
- Kovariansi (
+T): MemungkinkanContainer[Subtype]menjadi subtipe dariContainer[Supertype]. Berguna untuk tipe 'penghasil' yang hanya mengembalikanT. - Kontravariansi (
-T): MemungkinkanContainer[Supertype]menjadi subtipe dariContainer[Subtype]. Berguna untuk tipe 'pengonsumsi' yang hanya menerimaTsebagai masukan. - Invariansi: Perilaku bawaan. Tipe harus benar-benar sama.
Memahami variansi membantu Anda membuat kode generik di Scala yang lebih fleksibel dan aman terhadap tipe!
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Varians: Kovarians & Kontravarians” gratis?
Ya — teks lengkap “Varians: Kovarians & Kontravarians” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Scala for Backend Engineering & Functional Programming, upgrade ke CoddyKit PRO. Kursus Scala for Backend Engineering & Functional Programming mencakup 3 pelajaran total.
Apa yang akan aku pelajari di “Varians: Kovarians & Kontravarians”?
Kuasai kovarians dan kontravarians untuk menangani hubungan subtipe dalam tipe generik dengan benar. Kamu berlatih Scala for Backend Engineering & Functional Programming 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 Scala for Backend Engineering & Functional Programming?
Tidak diperlukan pengalaman sebelumnya. Scala for Backend Engineering & Functional Programming 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 3.
Berapa lama pelajaran “Varians: Kovarians & Kontravarians” 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 Scala for Backend Engineering & Functional Programming ini?
Ya. Setiap pelajaran Scala for Backend Engineering & Functional Programming 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
- Generik dan Parameter Tipe
- Varians: Kovarians & Kontravarians
- Kelas Tipe dan Implisit