Interface & Polymorphism — Go

Interface di Go Interface adalah kontrak perilaku — ia mendefinisikan method apa saja yang harus dimiliki sebuah tipe, tanpa peduli bagaimana implementasinya…

Interface adalah daftar method yang harus dimiliki sebuah tipe, tanpa peduli isinya. Bayangin mesin kasir Warung Bakso Mas Bimo yang menanyakan satu hal ke tiap baris struk: "totalmu berapa?". Pesanan bakso, ongkos kirim, atau apa pun boleh masuk struk, asal bisa menjawab pertanyaan itu. Gak ada yang perlu mendaftar dulu: punya jawabannya, berarti boleh masuk.

🧪 Tebakan cepat: var i any = "halo", lalu s, ok := i.(int) dan fmt.Println(s, ok). Apa yang keluar: program berhenti dengan panic, atau tercetak 0 false? Tebak dulu, jawabannya ada di bagian "any dan type assertion".

Interface: daftar method yang diminta

package main

import "fmt"

type Tagihan interface {
	Total() int
}

type Pesanan struct {
	Menu  string
	Harga int
	Porsi int
}

func (p Pesanan) Total() int {
	return p.Harga * p.Porsi
}

type Ongkir struct {
	Jarak int
}

func (o Ongkir) Total() int {
	return 2000 * o.Jarak
}

func totalBayar(daftar []Tagihan) int {
	jumlah := 0
	for _, t := range daftar {
		jumlah += t.Total()
	}
	return jumlah
}

func main() {
	daftar := []Tagihan{
		Pesanan{Menu: "Bakso urat", Harga: 15000, Porsi: 2},
		Ongkir{Jarak: 3},
	}
	for _, t := range daftar {
		fmt.Printf("%T %d\n", t, t.Total())
	}
	fmt.Println("total:", totalBayar(daftar))
}
$ go run bakso.go
main.Pesanan 30000
main.Ongkir 6000
total: 36000

Kontraknya dicek compiler. Hapus method Total dari Ongkir, dan Go menolak:

cannot use Ongkir{…} (value of struct type Ongkir) as Tagihan value in array or slice literal: Ongkir does not implement Tagihan (missing method Total)

Tulis Total() float64 dan alasannya jadi (wrong type for method Total), diikuti have Total() float64 dan want Total() int. Nama, parameter, dan tipe hasilnya harus persis sama.

Latihan memakai pola yang sama: interface Shape dengan Area() float64, Circle{R: 5} (3,14 × 5 × 5 = 78,5) dan Rectangle{W: 4, H: 6} (4 × 6 = 24), lalu totalArea(shapes []Shape) float64 menjumlahkan semuanya jadi 102.5.

Isi sebuah nilai interface: tipe dan nilai

Variabel bertipe interface menyimpan dua hal: tipe asli dan nilainya. t di putaran pertama berisi (Pesanan, {Bakso urat 15000 2}), di putaran kedua (Ongkir, {3}). Saat t.Total() dipanggil, Go menjalankan method milik tipe aslinya.

any dan type assertion

Interface tanpa method, interface{}, dipenuhi semua tipe. Sejak Go 1.18 namanya punya alias pendek: any. Variabel any bisa menampung apa saja, tapi Go gak tahu isinya, jadi kamu gak bisa langsung memakainya: var x any = 42 lalu x + 1 ditolak dengan invalid operation: x + 1 (mismatched types any and untyped int).

Untuk mengambil nilai aslinya, pakai type assertion x.(T):

var x any = "halo"

s := x.(string)  // "halo"
n, ok := x.(int) // 0 false: bukan int, tapi gak panic
m := x.(int)     // panic
panic: interface conversion: interface {} is string, not int

Type switch

Mau menangani beberapa tipe sekaligus? Pakai type switch:

func jelaskan(x any) {
	switch v := x.(type) {
	case string:
		fmt.Println("teks:", v)
	case int:
		fmt.Println("angka:", v+1)
	default:
		fmt.Printf("tipe lain: %T\n", v)
	}
}

jelaskan("halo") mencetak teks: halo, jelaskan(42) mencetak angka: 43 (di cabang int, v sudah bertipe int, jadi bisa dijumlah), dan jelaskan(Ongkir{Jarak: 3}) mencetak tipe lain: main.Ongkir.

📌 Interface nil dan interface berisi pointer nil

Interface baru bernilai nil kalau tipe dan nilainya sama-sama kosong:

var p *Ongkir      // pointer nil
var t Tagihan = p  // tipe: *Ongkir, nilai: nil
fmt.Println(p == nil, t == nil) // true false

t sudah punya tipe (*Ongkir), jadi t == nil bernilai false walau isinya pointer nil. Ini sering bikin if err != nil lolos padahal gak ada error, kalau sebuah fungsi mengembalikan pointer nil sebagai error.

⚠️ Jebakan yang sering ketemu

TL;DR: Interface adalah daftar method: type Tagihan interface { Total() int }. Tipe apa pun yang punya method itu otomatis memenuhinya, tanpa implements, dan compiler menolak tipe yang kurang method (missing method Total). Fungsi yang menerima []Tagihan bisa memanggil Total() di tipe apa pun (polymorphism), seperti totalArea di latihan yang menjumlahkan Circle dan Rectangle jadi 102.5. Nilai interface menyimpan tipe asli dan nilainya. any (interface{}) menampung apa saja, tapi isinya diambil dengan type assertion: x.(int) panic kalau salah, n, ok := x.(int) memberi 0 false. Type switch switch v := x.(type) menangani beberapa tipe. Interface berisi pointer nil gak sama dengan nil.

Yang akan kamu pelajari