Covariance dan Contravariance

AndriawanAndriawan
4 min read

Sebagai developer sangat penting untuk bisa memahami konsep Covariance dan Contravariance pada konsep Polymorphism dan inheritance/turunan. Karena pada konsep ini terdapat aturan-aturan yang harus diikuti ketika diperlukan kemanan dari tipe-tipe return atau argument.

Coba perhatikan gambar di bawah ini.

Gambar 1 diambil dari Wiki

Invariant ( Kiri )

Pada gambar 1 terlihat pada sisi Kiri. Aturan yang harus dipatuhi pada subtype-nya (ClassB) tidak ada perubahan signature method baik Nilai argument atau nilai return dari method. Jadi masih mengikuti supertype-nya. Tapi mungkin ada perbedaan pada detail-detail logic method lainnya.

class KetuaKelas {
    async order(): Promise<OrderResponse> {
        const result = await this.executeOrder()
        return result as OrderResponse
    }

    async executeOrder() {
       // Execute 
    }
}

class WakilKetua extends KetuaKelas {
   async order(): Promise<OrderResponse> {
      const granted = await this.askPermissionKetua()
      if (!granted) {
           throw new Error("Unapproved")
      }

      const result = await this.executeOrder()
      return result as OrderResponse
   }

   async executeOrder() {
       // Execute 
   }

   async askPermissionKetua() {
      return true
   }
}

Pada code terdapat dua class yaitu KetuaKelas dan WakilKetua dimana KetuaKelas merupakan Supertype dan WakilKetua adalah Subtype terlihat dari extends keyword pada class WakilKetua.

Pada class KetuaKelas terdapat method dengan nama order dan juga executeOrder. Dimana method ini juga digunakan pada class WakilKetua. Juga baik di class KetuaKelas dan juga WakilKetua tidak ada perubahan argument maupun return method. Inilah yang disebut Invariant. Method di Subtype tidak merubah signature dari method di Supertype-nya.

Covariance ( Tengah )

Pada gambar 1 bagian Tengah disebut covariance, karena return type pada subtype tidak memiliki nilai yang lebih general atau lebih luas dari pada supertype nya. Aturan ini digunakan untuk menjaga output type pada suatu process. Sebagai contoh

let keranjang: Buah[] = []

class Jambu implement Buah {}

let obj = Jambu

Pada contoh code tersebut, let keranjang dengan tipe data array dimana isinya adalah kumpulan buah, oleh karena itu pendefinisian pada variable ini adalah Buah[]. Buah disini adalah supertypenya. Ketika obj dari Jambu akan dimasukan ke dalam kerajang. Dimana Jambu adalah bagian dari Buah, maka Jambu boleh dimasukan ke dalam Keranjang. Karena Jambu bagian dari Buah maka Jambu merupakan subtype dari Buah.

Lalu bagaimana dalam turunan/inheritance? sebagai contoh terdapat code seperti ini

type Buah = {
   berbiji: boolean
}
type Jeruk = Buah & { berbiji: boolean }

class Keranjang {
  sudahMatang(): Buah {}
}

class Bungkus extends Keranjang {
   sudahMatang(): Jeruk {}
}

Pada code tersebut terdapat dua type yaitu Buah dan Jeruk dimana Buah digunakan sebagai return type method sudahMatang pada class Keranjang, sedangkan Jeruk digunakan sebagai return type method sudahMatang pada class Bungkus. Dimana Bungkus adalah turunan dari Keranjang.

Seperti yang kita lihat pada code bahwa pada class Bungkus memiliki method yang sama dengan *parent class-nya, yaitu sudahMatang. Tapi yang membedakan adalah return type pada masing-masing class.

Terlihat bahwa return method pada class Bungkus memiliki nilai lebih spesifik (T’) dari pada return type method (T) pada parent classnya yaitu Keranjang. Maka dari itu dilihat dari arah panah inheritance dan juga degree dari return type searah. Sehingga disebut covariance dengan tujuannya adalah untuk mengamankan return type.

Contravariance (Kanan)

Sisi Kanan pada gambar 1**,* disebut Contravariance, dimana nilai Argument yang diikutkan dalam method dari subtype (T) memiliki nilai lebih general atau luas dari pada supertype-nya (T’), disebut sebagai Contravariance. Aturan ini digunakan untuk menjaga type dari argument yang di-override* oleh subtypenya.

Sebagai contoh kita bisa lihat code berikut

type Buah = {
   berbiji: boolean
}
type Jeruk = Buah & { berbiji: boolean }

class Keranjang {
  sudahMatang(item: Jeruk){} 
}

class Bungkus extends Keranjang {
   sudahMatang(item: Buah) {}
}

Code tersebut mirip dengan code sebelumnya pada bagian pembahasan Covariance. Perbedaannya adalah Convariance mengatur return type sedangkan Contravariance mengatur Argument. Selain itu, pada covariance nilai yang lebih general ada pada Supertype-nya, sedangkan pada Contravariance sebaliknya. Nilai yang lebih general ada pada subtype-nya. Oleh karena itu arah panahnya berlawanan.

Kesimpulan

Ketika membuat code, kita tidak selalu perlu mengimplentasikan Covariance atau Contravariance. Kita memerlukannya hanya pada saat memang diperlukan pengamanan pada tipe data kembalian method atau parameter. Agar integritas nilai yang masuk maupun keluar cukup terjaga.

0
Subscribe to my newsletter

Read articles from Andriawan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Andriawan
Andriawan