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 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#

BahasaCara Implement Interface
Javaclass Circle implements Shape { ... } — eksplisit
C#class Circle : IShape { ... } — eksplisit
GoCukup 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

Yang akan kamu pelajari