Interface Go Tingkat Lanjut di 2026: Komposisi, Type Assertion, dan Pertanyaan Interview

Panduan mendalam tentang interface Go modern: komposisi interface, type assertion yang aman, generic self-referential, dan pertanyaan interview yang sering muncul untuk developer Go.

Interface Go tingkat lanjut: komposisi, type assertion, dan pertanyaan interview

Interface Go mendefinisikan perilaku tanpa menentukan implementasi, menjadikannya inti dari penulisan kode yang fleksibel dan mudah diuji. Go 1.26 memperluas kemampuan ini dengan generic self-referential, iterator refleksi baru, dan penanganan error yang type-safe melalui errors.AsType. Panduan ini membahas komposisi interface, type assertion, pola embedding, dan pertanyaan-pertanyaan yang sering muncul dalam interview teknis.

Peningkatan Interface Go 1.26

Tipe generic sekarang dapat mereferensikan dirinya sendiri dalam daftar parameter tipe: type Adder[A Adder[A]] interface { Add(A) A }. Pola F-bounded polymorphism ini memungkinkan interface yang membatasi tipe return ke tipe yang mengimplementasikannya.

Komposisi Interface dan Pola Embedding

Embedding interface menggabungkan beberapa interface menjadi satu kontrak. Hasilnya adalah interface baru yang memerlukan semua method dari interface yang di-embed. Pola ini menghindari duplikasi dan menciptakan batas abstraksi yang jelas.

interfaces.gogo
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

// Interface komposisi yang meng-embed tiga interface
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

Setiap tipe yang mengimplementasikan method Read, Write, dan Close secara otomatis memenuhi ReadWriteCloser. Library standar Go menggunakan pola ini secara ekstensif dalam package io.

Pertanyaan interview yang umum meminta kandidat menjelaskan mengapa Go lebih memilih interface yang kecil dan fokus. Jawabannya terletak pada komposabilitas: io.Reader muncul di ratusan fungsi karena hanya membutuhkan satu method. Interface yang lebih besar menciptakan coupling yang lebih ketat dan mengurangi reusability.

Type Assertion: Sintaks dan Keamanan

Type assertion mengekstrak tipe konkret dari nilai interface. Bentuk dua-nilai mencegah panic dengan mengembalikan boolean yang menunjukkan keberhasilan.

assertions.gogo
func processValue(v interface{}) {
    // Assertion dua-nilai: aman, tanpa panic
    if str, ok := v.(string); ok {
        fmt.Printf("Nilai string: %s\n", str)
        return
    }
    
    // Type switch untuk multiple tipe
    switch val := v.(type) {
    case int:
        fmt.Printf("Integer: %d\n", val)
    case float64:
        fmt.Printf("Float: %.2f\n", val)
    case []byte:
        fmt.Printf("Bytes: %x\n", val)
    default:
        fmt.Printf("Tipe tidak dikenal: %T\n", val)
    }
}

Type switch menangani multiple tipe dengan rapi. Setiap case mengikat val ke tipe yang di-assert dalam bloknya, menghilangkan kebutuhan untuk assertion terpisah.

Risiko Panic

Assertion satu-nilai seperti str := v.(string) akan panic jika assertion gagal. Kode production harus selalu menggunakan bentuk dua-nilai atau type switch.

Pewawancara sering bertanya tentang performa type assertion. Runtime melakukan satu perbandingan type descriptor, membuat assertion tidak mahal. Biaya meningkat dengan nilai interface yang membungkus pointer ke struct besar karena indirection, tetapi assertion itu sendiri tetap O(1).

Generic Self-Referential di Go 1.26

Go 1.26 memperkenalkan parameter tipe self-referential, memungkinkan interface dimana method harus mengembalikan tipe yang mengimplementasikannya. Pola ini, kadang disebut F-bounded polymorphism, menyelesaikan keterbatasan yang sudah lama ada.

builder.gogo
// Interface self-referential: method mengembalikan tipe yang sama
type Builder[B Builder[B]] interface {
    WithName(name string) B
    WithAge(age int) B
    Build() string
}

type PersonBuilder struct {
    name string
    age  int
}

func (p PersonBuilder) WithName(name string) PersonBuilder {
    p.name = name
    return p
}

func (p PersonBuilder) WithAge(age int) PersonBuilder {
    p.age = age
    return p
}

func (p PersonBuilder) Build() string {
    return fmt.Sprintf("%s, %d tahun", p.name, p.age)
}

// Fungsi generic menggunakan constraint self-referential
func configure[B Builder[B]](b B, name string, age int) string {
    return b.WithName(name).WithAge(age).Build()
}

Constraint Builder[B] memastikan bahwa WithName dan WithAge mengembalikan B, bukan generic Builder. Tanpa ini, tipe return akan menjadi interface, kehilangan informasi tipe konkret dan merusak method chaining.

Tipe matematika mendapat manfaat dari pola ini. Interface Addable[A Addable[A]] memastikan Add(A) A mengembalikan tipe numerik yang sama, mencegah pencampuran nilai BigInt dan Decimal secara tidak sengaja.

errors.AsType: Error Unwrapping Type-Safe

Go 1.26 menambahkan errors.AsType, menggantikan pola pointer-dance dari errors.As. Versi generic mengembalikan error yang di-unwrap secara langsung.

errors_handling.gogo
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validasi gagal pada %s: %s", e.Field, e.Message)
}

func handleError(err error) {
    // Go 1.26: generic type-safe unwrapping
    if valErr, ok := errors.AsType[*ValidationError](err); ok {
        log.Printf("Error validasi pada field %s\n", valErr.Field)
        return
    }
    
    // Sebelum Go 1.26: pointer-based unwrapping
    // var valErr *ValidationError
    // if errors.As(err, &valErr) { ... }
    
    log.Printf("Error tidak terduga: %v\n", err)
}

API baru ini menghilangkan deklarasi variabel terpisah dan membuat tipe target eksplisit dalam pemanggilan fungsi. Error chain dicari dengan cara yang sama seperti sebelumnya; hanya interface yang berubah.

Siap menguasai wawancara Go Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Iterator Refleksi untuk Inspeksi Interface

Go 1.26 menambahkan method iterator ke package reflect. Type.Methods() dan Value.Methods() mengembalikan iterator untuk ranging over method, menggantikan loop berbasis index.

reflection.gogo
import "reflect"

func inspectInterface(v interface{}) {
    t := reflect.TypeOf(v)
    
    // Go 1.26: inspeksi method berbasis iterator
    fmt.Printf("Method tipe %s:\n", t.Name())
    for method := range t.Methods() {
        fmt.Printf("  %s: %s\n", method.Name, method.Type)
    }
    
    // Untuk field struct (juga baru di 1.26)
    if t.Kind() == reflect.Struct {
        for field := range t.Fields() {
            fmt.Printf("  Field: %s (%s)\n", field.Name, field.Type)
        }
    }
}

Pola iterator sejalan dengan fitur range-over-function di Go 1.23. Kode menjadi lebih mudah dibaca, dan tidak ada penalti performa: iterator menghasilkan nilai secara lazy.

Kepuasan Interface pada Compile Time

Go memeriksa kepuasan interface pada compile time ketika menetapkan nilai konkret ke variabel interface. Pemeriksaan eksplisit menggunakan assignment blank identifier menangkap error lebih awal dalam codebase besar.

compile_check.gogo
type Storage interface {
    Save(key string, data []byte) error
    Load(key string) ([]byte, error)
    Delete(key string) error
}

type FileStorage struct {
    basePath string
}

// Pemeriksaan compile-time: gagal jika FileStorage kehilangan method
var _ Storage = (*FileStorage)(nil)

func (f *FileStorage) Save(key string, data []byte) error {
    path := filepath.Join(f.basePath, key)
    return os.WriteFile(path, data, 0644)
}

func (f *FileStorage) Load(key string) ([]byte, error) {
    path := filepath.Join(f.basePath, key)
    return os.ReadFile(path)
}

func (f *FileStorage) Delete(key string) error {
    return os.Remove(filepath.Join(f.basePath, key))
}

Baris var _ Storage = (*FileStorage)(nil) hanya compile jika *FileStorage memenuhi Storage. Pola ini menangkap method yang hilang segera daripada pada runtime ketika nilai ditetapkan.

Empty Interface dan Type Constraint

Empty interface interface{} menerima nilai apapun, sementara any adalah aliasnya sejak Go 1.18. Generic constraint menyediakan keamanan tipe compile-time tanpa runtime assertion.

constraints.gogo
import "golang.org/x/exp/constraints"

// Fungsi generic dengan constraint numerik
func Sum[T constraints.Integer | constraints.Float](values []T) T {
    var total T
    for _, v := range values {
        total += v
    }
    return total
}

// Constraint comparable untuk map key
func Contains[K comparable, V any](m map[K]V, key K) bool {
    _, exists := m[key]
    return exists
}

Lebih memilih generic daripada interface{} menghilangkan type assertion dan menangkap ketidakcocokan tipe pada compile time. Trade-off-nya adalah kompleksitas tambahan dalam signature fungsi, sehingga generic paling cocok ketika fungsi benar-benar beroperasi pada multiple tipe.

Pertanyaan Interview Teknis tentang Interface Go

Pewawancara menguji pengetahuan interface di berbagai tingkat. Berikut adalah pertanyaan umum dengan jawaban ringkas.

T: Apa yang terjadi ketika memanggil method pada nilai interface nil versus nilai konkret nil di dalam interface?

Interface nil tidak memiliki tipe dan nilai; memanggil method apapun akan panic. Interface non-nil yang berisi pointer nil memiliki tipe; method dijalankan dengan receiver nil. Perilaku ini memungkinkan pola seperti (*bytes.Buffer)(nil).String() yang mengembalikan string kosong.

T: Bagaimana perbandingan interface bekerja?

Dua nilai interface sama jika memiliki tipe dinamis yang sama dan nilai dinamis yang sama. Membandingkan interface dengan tipe yang tidak comparable (slice, map, function) akan panic pada runtime.

T: Kapan method harus menggunakan pointer receiver versus value receiver?

Pointer receiver memungkinkan mutasi dan menghindari penyalinan struct besar. Value receiver aman untuk penggunaan concurrent dan bekerja dengan nilai dan pointer. Jika ada method yang membutuhkan pointer receiver, semua method pada tipe tersebut harus menggunakan pointer receiver untuk konsistensi, karena hanya *T yang memenuhi interface yang memerlukan method pointer-receiver.

receiver_rules.gogo
type Counter struct {
    count int
}

// Pointer receiver: memodifikasi struct
func (c *Counter) Increment() {
    c.count++
}

// Value receiver: operasi read-only
func (c Counter) Value() int {
    return c.count
}

type Incrementer interface {
    Increment()
}

func main() {
    var c Counter
    // var i Incrementer = c  // Error compile: Counter tidak memiliki Increment
    var i Incrementer = &c     // OK: *Counter memiliki Increment
    i.Increment()
}

T: Jelaskan prinsip "accept interfaces, return structs".

Fungsi yang menerima interface terlepas dari implementasi konkret, memungkinkan testing dengan mock. Mengembalikan tipe konkret memberikan pemanggil akses penuh ke method tipe tersebut tanpa type assertion. Mengembalikan interface menyembunyikan penambahan method di masa depan di balik type assertion.

Untuk persiapan interview Go lebih lanjut, lihat modul pertanyaan concurrency Go dan modul testing.

Tips Interview

Ketika diminta mendesain interface, mulai dengan kasus single-method. Perluas hanya ketika multiple method selalu dipanggil bersama. Interface Stringer, Reader, dan Handler di library standar masing-masing mendefinisikan satu method.

Yang Perlu Diketahui Developer Go Senior tentang Interface

  • Komposisi interface melalui embedding menciptakan kontrak fleksibel tanpa hierarki inheritance. Jaga interface tetap kecil: satu sampai tiga method mencakup sebagian besar kasus penggunaan.
  • Type assertion dan type switch mengekstrak tipe konkret dengan aman. Selalu gunakan bentuk dua-nilai atau switch di kode production untuk menghindari panic.
  • Generic self-referential Go 1.26 memungkinkan pola builder dan tipe matematika dimana method harus mengembalikan tipe yang mengimplementasikannya.
  • errors.AsType menyederhanakan error unwrapping dengan API generic yang type-safe. Gunakan di kode baru yang menargetkan Go 1.26 atau lebih baru.
  • Pemeriksaan interface compile-time dengan var _ Interface = (*Type)(nil) menangkap method yang hilang sebelum test dijalankan.
  • Lebih pilih generic daripada interface{} ketika keamanan tipe pada compile time lebih penting dari kompleksitas signature.
  • Pedoman "accept interfaces, return structs" menjaga API fleksibel untuk pemanggil sambil mengekspos fungsionalitas penuh.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Tantangan harian

Bisakah kamu menemukan bug di Go?

Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Anthony Fillion-Maillet

Ditulis oleh

Anthony Fillion-Maillet

Pendiri SharpSkill

Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.

Diperbarui 10 September 2026

Tag

#go
#interfaces
#generics
#type-assertion
#interview

Bagikan

Artikel terkait