Interface di Go
Interface adalah kontrak perilaku — ia mendefinisikan method apa saja yang harus dimiliki sebuah tipe, tanpa peduli bagaimana implementasinya. Ini tulang punggung polymorphism di Go.
Mendefinisikan Interface
type Shape interface {
Area() float64
Perimeter() float64
}
Setiap tipe yang punya method Area() float64 dan Perimeter() float64 otomatis memenuhi interface Shape. Tidak ada keyword implements — Go menggunakan implicit satisfaction.
Implicit Implementation
type Circle struct { R float64 }
func (c Circle) Area() float64 { return 3.14 * c.R * c.R }
func (c Circle) Perimeter() float64 { return 2 * 3.14 * c.R }
type Rectangle struct { W, H float64 }
func (r Rectangle) Area() float64 { return r.W * r.H }
func (r Rectangle) Perimeter() float64 { return 2 * (r.W + r.H) }
// Keduanya otomatis memenuhi Shape — tidak perlu deklarasi apapun!
func describe(s Shape) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}
describe(Circle{R: 5})
describe(Rectangle{W: 3, H: 4})
Perbandingan dengan Java/C#
| Bahasa | Cara Implement Interface |
|---|---|
| Java | class Circle implements Shape { ... } — eksplisit |
| C# | class Circle : IShape { ... } — eksplisit |
| Go | Cukup punya method yang cocok — tidak perlu deklarasi apapun |
Keunggulan: kamu bisa membuat tipe dari package lain memenuhi interface lokal tanpa ubah kode mereka — sangat powerful untuk dependency inversion.
Interface Standar: Stringer, error, io
// fmt.Stringer — otomatis dipanggil saat fmt.Println
type Stringer interface {
String() string
}
// error — interface paling umum di Go
type error interface {
Error() string
}
// io.Reader, io.Writer — banyak dipakai
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
Dengan implement String(), struct-mu otomatis bekerja dengan fmt.Println:
type Person struct { Name string; Age int }
func (p Person) String() string {
return fmt.Sprintf("%s (%d)", p.Name, p.Age)
}
fmt.Println(Person{"Budi", 25}) // "Budi (25)"
Empty Interface: interface{} / any
Interface kosong bisa menampung nilai tipe apapun — mirip Object di Java atau any di TypeScript. Sejak Go 1.18 alias any lebih disukai:
var x any = 42
x = "halo"
x = []int{1, 2, 3}
// Semua valid — tapi kamu tidak bisa panggil method tanpa type assertion
Type Assertion
Mengekstrak tipe konkret dari interface value:
var i any = "halo"
s := i.(string) // langsung — panic jika bukan string
fmt.Println(s)
// Comma-ok idiom — aman, tidak panic
s, ok := i.(string)
if ok {
fmt.Println("string:", s)
}
n, ok := i.(int)
if !ok {
fmt.Println("bukan int")
}
Type Switch
Cara elegan untuk handle banyak tipe:
func describe(i any) string {
switch v := i.(type) {
case int:
return fmt.Sprintf("int: %d", v)
case string:
return fmt.Sprintf("string: %q", v)
case []int:
return fmt.Sprintf("slice int dengan %d elemen", len(v))
case nil:
return "nil"
default:
return fmt.Sprintf("tipe lain: %T", v)
}
}
Interface Composition
Interface bisa dibangun dari interface lain (mirip embedding struct):
type Reader interface { Read(p []byte) (int, error) }
type Writer interface { Write(p []byte) (int, error) }
// Komposisi — ReadWriter harus memenuhi Read dan Write
type ReadWriter interface {
Reader
Writer
}
Best Practices
- Kecil itu bagus — interface Go idealnya 1-3 method.
io.Readerhanya 1 method! - Define di sisi consumer — interface didefinisikan di tempat yang butuh, bukan tempat implementasinya
- Return concrete type, accept interface — fungsi return struct nyata, parameter pakai interface
- Hindari
anykecuali terpaksa — lebih baik interface kecil yang spesifik