0Pricing
Scala for Backend Engineering & Functional Programming · Pelajaran

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 A adalah subtipe dari B, maka Box[A] menjadi subtipe dari Box[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 memberikan Box[Cat] karena tipe tersebut 'menghasilkan' sesuatu yang lebih spesifik (yaitu Cat, yang merupakan Animal).

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 memperlakukan List[Cat] sebagai List[Animal] karena Anda hanya 'membaca' Animal atau subtipenya dari dalam koleksi tersebut.
  • Anda tidak dapat menambahkan Animal sembarang ke List[Cat] jika koleksi itu diberi tipe List[Animal]. Hal ini menjaga keamanan tipe.

Kontravariansi: Tipe 'Consumer' (-T)

Kontravariansi ditandai dengan menempatkan - sebelum parameter tipe (misalnya, trait Consumer[-T]).

  • Jika A adalah subtipe dari B, maka Consumer[B] menjadi subtipe dari Consumer[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 memberikan Consumer[Animal] karena tipe tersebut 'mengonsumsi' sesuatu yang lebih umum dan dapat menangani Animal apa pun, termasuk Cat.

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 Animal apa pun (Animal => Unit), fungsi tersebut tentu dapat memproses Cat. 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 dari Box[B] jika A dan B merupakan 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 A bersifat 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 B bersifat 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): Memungkinkan Container[Subtype] menjadi subtipe dari Container[Supertype]. Berguna untuk tipe 'penghasil' yang hanya mengembalikan T.
  • Kontravariansi (-T): Memungkinkan Container[Supertype] menjadi subtipe dari Container[Subtype]. Berguna untuk tipe 'pengonsumsi' yang hanya menerima T sebagai 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

  1. Generik dan Parameter Tipe
  2. Varians: Kovarians & Kontravarians
  3. Kelas Tipe dan Implisit
← Kembali ke Scala for Backend Engineering & Functional Programming