Interface di Go
Interface di Go bersifat implicit — kamu tidak perlu menulis implements InterfaceName. Selama sebuah struct memiliki semua method yang diperlukan, struct tersebut secara otomatis mengimplementasi interface tersebut.
// Definisi interface
type Stringer interface {
String() string
}
type Animal interface {
Sound() string
Name() string
}
// Struct yang mengimplementasi Animal
type Dog struct{ nama string }
type Cat struct{ nama string }
func (d Dog) Sound() string { return "Woof!" }
func (d Dog) Name() string { return d.nama }
func (c Cat) Sound() string { return "Meow!" }
func (c Cat) Name() string { return c.nama }
// Fungsi yang menerima interface
func describe(a Animal) {
fmt.Printf("%s berkata: %s\n", a.Name(), a.Sound())
}
func main() {
anjing := Dog{nama: "Rex"}
kucing := Cat{nama: "Whiskers"}
describe(anjing) // Rex berkata: Woof!
describe(kucing) // Whiskers berkata: Meow!
// Slice of interface
hewan := []Animal{anjing, kucing}
for _, h := range hewan {
describe(h)
}
}
Empty Interface dan Type Assertion
// interface{} (atau 'any' di Go 1.18+) menerima tipe apapun
func printApa(nilai interface{}) {
fmt.Println(nilai)
}
printApa("teks") // teks
printApa(42) // 42
printApa(true) // true
// Type assertion — cek tipe konkret dari interface
var i interface{} = "hello"
s, ok := i.(string) // safe assertion
if ok {
fmt.Println("String:", s) // String: hello
}
n, ok := i.(int) // gagal tapi aman
if !ok {
fmt.Println("Bukan int, n =", n) // Bukan int, n = 0
}
// Type switch
func cekTipe(val interface{}) {
switch v := val.(type) {
case string:
fmt.Printf("String dengan panjang %d\n", len(v))
case int:
fmt.Printf("Integer: %d\n", v)
case bool:
fmt.Printf("Boolean: %v\n", v)
default:
fmt.Printf("Tipe tidak dikenal: %T\n", v)
}
}
Error Handling di Go
Go tidak pakai try-catch. Error adalah nilai biasa yang di-return dari fungsi. Ini terasa verbose di awal, tapi membuat kode lebih jelas dan tidak ada kejutan tersembunyi.
import (
"errors"
"fmt"
"strconv"
)
func bagi(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("pembagi tidak boleh nol")
}
return a / b, nil
}
func main() {
hasil, err := bagi(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Hasil:", hasil)
// Konversi string ke int — bisa error
angka, err := strconv.Atoi("bukan angka")
if err != nil {
fmt.Println("Gagal konversi:", err)
} else {
fmt.Println("Angka:", angka)
}
}
Custom Error
// Custom error dengan struct
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validasi gagal pada field '%s': %s", e.Field, e.Message)
}
func validateTask(title string) error {
if title == "" {
return &ValidationError{Field: "title", Message: "tidak boleh kosong"}
}
if len(title) > 100 {
return &ValidationError{Field: "title", Message: "maksimal 100 karakter"}
}
return nil
}
func main() {
err := validateTask("")
if err != nil {
var valErr *ValidationError
if errors.As(err, &valErr) {
fmt.Println("Field:", valErr.Field)
fmt.Println("Pesan:", valErr.Message)
}
}
}
fmt.Errorf dan %w (Error Wrapping)
func getTask(id int) (*Task, error) {
task, err := db.FindByID(id)
if err != nil {
// Wrap error dengan context tambahan
return nil, fmt.Errorf("getTask(%d): %w", id, err)
}
return task, nil
}
// Unwrap error dengan errors.Is dan errors.As
var ErrNotFound = errors.New("not found")
err := fmt.Errorf("database error: %w", ErrNotFound)
fmt.Println(errors.Is(err, ErrNotFound)) // true
Defer, Panic, dan Recover
// Defer — jalankan sebelum fungsi return (biasa untuk cleanup)
func bacaFile(path string) {
file, err := os.Open(path)
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close() // dipanggil saat fungsi selesai, apapun yang terjadi
// ... proses file
fmt.Println("File berhasil dibaca")
}
// Panic — error kritis yang menghentikan program
func mustDivide(a, b int) int {
if b == 0 {
panic("pembagi tidak boleh nol!") // program crash
}
return a / b
}
// Recover — tangkap panic (seperti try-catch, tapi jarang dipakai)
func safeDiv(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered: %v", r)
}
}()
result = mustDivide(a, b)
return
}
💡 Tips: Gunakan panic hanya untuk situasi yang benar-benar tidak bisa dipulihkan (bug programmer). Untuk error normal seperti validasi atau file not found, selalu gunakan return error biasa.
Komentar 0