Type assertion memeriksa satu dugaan tipe pada satu waktu. Begitu sebuah nilai any bisa berisi lima atau enam tipe berbeda, rantai if v, ok := x.(T); ok bertingkat membuat kode berubah menjadi tangga panjang yang sulit dibaca. Go menyediakan bentuk khusus untuk masalah ini: type switch, statement switch yang cabangnya dinomori oleh tipe, bukan oleh nilai. Sintaks dasarnya sudah kita kenal di chapter Type Switch
pada Section Kendali Alur; chapter ini membedah sisi interfacenya — tipe static variabel di tiap cabang, pencocokan nil, dan pemakaian nyata sebagai formatter.
Satu Cabang per Tipe #
Bentuk lengkapnya seperti ini:
switch v := x.(type) {
case int:
// v bertipe int di sini
case string:
// v bertipe string di sini
case []int:
// v bertipe []int di sini
case nil:
// x adalah interface nil sejati
default:
// tipe lain yang tidak didaftar
}Perhatikan dua hal. Pertama, operand switch ditulis x.(type) — kata kunci type menggantikan tipe sasaran, dan tanda kurung wajib. Kedua, v := mendeklarasikan variabel yang tipenya berbeda di setiap cabang: compiler mengganti tipe v sesuai tipe yang tertulis pada case. Type switch pada dasarnya adalah kumpulan type assertion comma-ok yang dirangkai, dengan satu pemeriksaan berurutan dan tanpa risiko panic.
Tipe Static v di Setiap Cabang #
Aturan tipe v ini yang paling sering disalahpahami. Untuk case berisi satu tipe, v memang bertipe itu. Untuk case berisi beberapa tipe sekaligus, compiler tidak bisa memilih satu, sehingga v tetap bertipe interface aslinya (any jika operand any). Percobaan memperlakukan v sebagai bool di cabang case bool, float64 gagal dikompilasi dengan pesan berikut, diverifikasi nyata pada Go 1.27:
invalid operation: operator ! not defined on v (variable of interface type any)Tabel berikut merangkum aturannya:
Bentuk case | Tipe static v di dalam cabang |
|---|---|
case int | int |
case []int | []int |
case bool, float64 | tipe operand (any) |
case nil | tipe operand (any) |
default | tipe operand (any) |
case nil dan Interface Kosong #
Cabang case nil hanya cocok ketika interface yang diperiksa benar-benar nil: pasangan (dynamic type, dynamic value) kosong seluruhnya. Interface yang berisi pointer nil tidak akan masuk cabang ini karena dynamic type-nya masih terisi — pembedaan yang menjadi inti jebakan pada chapter Interface nil
. Jika operand type switch Anda bisa bernilai nil, daftarkan case nil secara eksplisit; tanpa cabang itu, nilai nil jatuh ke default dan sering kali berujung pada format yang membingungkan.
Program Demo: Formatter Sederhana #
Pemakaian nyata type switch yang paling umum adalah formatter: fungsi yang menerima data bertipe apa pun lalu memilih format cetak per tipe. Berkas typeswitch-demo.go membangun formatter tersebut:
package main
import "fmt"
// format memilih perlakuan berbeda untuk setiap tipe dinamis.
func format(v any) string {
switch nilai := v.(type) {
case nil:
return "(kosong)"
case int:
return fmt.Sprintf("bilangan bulat %d", nilai)
case string:
return fmt.Sprintf("teks %q", nilai)
case []int:
return fmt.Sprintf("slice int %v", nilai)
case bool, float64:
// case multi-tipe: nilai tetap bertipe any, bukan bool/float64.
return fmt.Sprintf("tipe lain: %v (%T)", nilai, nilai)
default:
return fmt.Sprintf("tipe tak dikenal: %T", nilai)
}
}
func main() {
nilai := []any{42, "belajar", []int{1, 2, 3}, true, 3.5, nil, struct{}{}}
for _, v := range nilai {
fmt.Println(format(v))
}
}Output yang Dihasilkan:
bilangan bulat 42
teks "belajar"
slice int [1 2 3]
tipe lain: true (bool)
tipe lain: 3.5 (float64)
(kosong)
tipe tak dikenal: struct {}Mari kita telaah baris demi baris program di atas:
- Baris 6–7 (
func format+switch nilai := v.(type)): fungsi menerimaany; variabelnilaidideklarasikan langsung di header switch dan tipenya ditentukan per cabang. - Baris 8–9 (
case nil): menangkap interface nil sejati; elemennilpada slice di baris 25 masuk ke sini dan tercetak sebagai(kosong). - Baris 10–13 (
case int,case string): di dalam kedua cabang,nilaibertipeintdanstring— verb%ddan%qsah tanpa assertion tambahan. - Baris 14–15 (
case []int): tipe slice juga bisa menjadi cabang;%vmencetak isinya[1 2 3]. - Baris 16–18 (
case bool, float64): cabang multi-tipe memaksanilaitetap bertipeany, sehingga hanya%vdan%Tyang bisa dipakai di baris 18. - Baris 19–20 (
default): menampung tipe yang tidak didaftar — struct kosongstruct{}{}di baris 25 tercetak sebagaistruct {}lewat verb%T. - Baris 25–28 (
for _, v := range nilai): iterasi slice[]anyberisi tujuh tipe berbeda; setiap elemen melewatiformatyang sama tanpa satu pun risiko panic.
Type switch menuntaskan pertanyaan “tipe apa yang sedang menempel”. Pertanyaan berikutnya justru lebih berbahaya: kapan sebuah interface layak disebut nil, dan mengapa interface berisi pointer nil tidak pernah nil? Jebakan klasik itu kita bongkar pada chapter berikutnya.
Referensi #
- Spesifikasi Go: Interface types — dasar tipe dan method set yang dirujuk type switch.
- Effective Go: Interfaces — pola pemakaian interface dan pemeriksaan tipenya.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
typeswitch-demo.go
Unduh berkaspackage main
import "fmt"
// format memilih perlakuan berbeda untuk setiap tipe dinamis.
func format(v any) string {
switch nilai := v.(type) {
case nil:
return "(kosong)"
case int:
return fmt.Sprintf("bilangan bulat %d", nilai)
case string:
return fmt.Sprintf("teks %q", nilai)
case []int:
return fmt.Sprintf("slice int %v", nilai)
case bool, float64:
// case multi-tipe: nilai tetap bertipe any, bukan bool/float64.
return fmt.Sprintf("tipe lain: %v (%T)", nilai, nilai)
default:
return fmt.Sprintf("tipe tak dikenal: %T", nilai)
}
}
func main() {
nilai := []any{42, "belajar", []int{1, 2, 3}, true, 3.5, nil, struct{}{}}
for _, v := range nilai {
fmt.Println(format(v))
}
}