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", lalus, ok := i.(int)danfmt.Println(s, ok). Apa yang keluar: program berhenti dengan panic, atau tercetak0 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
type Tagihan interface { Total() int }artinya: tipe apa pun yang punya methodTotal() intadalah sebuahTagihan.PesanandanOngkirgak pernah menyebutTagihan. Go gak punya kataimplementsseperti Java: punya method-nya, berarti otomatis memenuhi interface itu. Ini disebut implicit satisfaction.[]Tagihanboleh berisi campuran keduanya, dantotalBayarcukup memanggilt.Total()tanpa tahu tipe aslinya. Satu pemanggilan, perilaku berbeda per tipe: inilah polymorphism.%Tmencetak tipe asli di dalam interface:main.Pesanan, lalumain.Ongkir.
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
x.(string)berhasil karena isinya memang string.n, ok := x.(int)adalah bentuk comma-ok. Kalau tipenya salah,okjadifalsedanndapat zero valueint, yaitu0. Jadifmt.Println(n, ok)mencetak0 false, tanpa panic. Itu jawaban tebakannya.x.(int)tanpaokmenghentikan program saat tipenya salah:
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
- Signature method beda sedikit.
Total() float64padahal interface mintaTotal() intberartiwrong type for method Total. - Pointer receiver. Kalau method-nya
func (o *Ongkir) Total() int, yang memenuhi interface cuma&Ongkir{...}.Ongkir{...}ditolak dengan(method Total has pointer receiver). - Type assertion tanpa
ok. Tipe yang salah berarti panic. Pakaiv, ok := x.(T)kalau isinya belum pasti. anydi mana-mana. Compiler jadi gak bisa mengecek apa pun. Interface kecil yang jelas lebih baik:io.Readerdanfmt.Stringercuma punya satu method.
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.