Variabel bertipe interface dapat menampung nilai bertipe apa pun saat program berjalan — angka, teks, boolean, bahkan koleksi. Fleksibilitas ini berguna untuk data yang bentuknya baru diketahui saat runtime, misalnya hasil pembacaan JSON atau isi kolom database. Masalahnya muncul ketika kita perlu memperlakukan isi tersebut sesuai tipennya: angka perlu dihitung, teks perlu dihitung panjangnya, slice perlu dihitung elemennya. Type switch adalah alat Go untuk memeriksa tipe dinamis sebuah interface dalam satu struktur yang rapi. Pada chapter ini kita membedah bentuknya, variabel v yang berubah tipe di tiap cabang, serta penanganan nil.
Interface dan Tipe Dinamis #
Nilai interface terdiri dari dua bagian: tipe konkret dari nilai yang disimpan dan nilai itu sendiri. Bagian tipe inilah yang disebut tipe dinamis, karena bisa berbeda-beda setiap kali program berjalan meskipun deklarasi variabelnya tetap. Alias any sama dengan interface{} — tipe interface tanpa method sehingga sanggup menampung nilai bertipe apa pun. Konsep interface dibahas tuntas pada Section Interface
; untuk sekarang, cukup bayangkan any sebagai kotak berlabel yang isinya bisa berganti-ganti.
Bentuk type switch memakai type guard x.(type) sebagai ekspresi switch:
switch v := x.(type) {
case int:
fmt.Println("angka:", v)
case string:
fmt.Println("teks:", v)
}Tulisan x.(type) hanya sah di dalam switch — memakainya di tempat lain ditolak kompilator. Tidak seperti type assertion satu tipe seperti x.(int) yang membahas di chapter Type Assertion
, type switch memeriksa banyak kemungkinan sekaligus tanpa memicu panic ketika tipenya tidak cocok.
Variabel v Mengikuti Tipe Dinamis #
Perhatikan variabel v pada bentuk di atas. Di dalam case int, v bertipe int sehingga operasi aritmatika langsung sah. Di dalam case string, v bertipe string sehingga fungsi len bisa dipakai. Go mengubah tipe v sesuai cabang yang sedang aktif — inilah keunggulan utama type switch dibanding rangkaian type assertion terpisah. Program berikut mendeskripsikan enam nilai bertipe any yang berbeda:
package main
import "fmt"
func describe(nilai any) string {
switch v := nilai.(type) {
case nil:
return "nilai kosong (nil)"
case int:
return fmt.Sprintf("bilangan bulat bernilai %d", v)
case string:
return fmt.Sprintf("teks %q sepanjang %d karakter", v, len(v))
case bool:
return fmt.Sprintf("nilai boolean %t", v)
case []int:
return fmt.Sprintf("slice int dengan %d elemen", len(v))
default:
return fmt.Sprintf("tipe %T belum didukung fungsi describe", v)
}
}
func main() {
fmt.Println(describe(42))
fmt.Println(describe("Budi"))
fmt.Println(describe(true))
fmt.Println(describe(nil))
fmt.Println(describe([]int{10, 20, 30}))
fmt.Println(describe(3.14))
}Jalankan program dari direktori chapter dengan perintah berikut:
go run type-switch-demo.goOutput yang Dihasilkan:
bilangan bulat bernilai 42
teks "Budi" sepanjang 4 karakter
nilai boolean true
nilai kosong (nil)
slice int dengan 3 elemen
tipe float64 belum didukung fungsi describeMari kita telaah baris demi baris program di atas:
- Baris 5 (
func describe(nilai any) string): mendeklarasikan fungsi dengan parameternilaibertipeanydan nilai balikstring. Fungsi ini bisa menerima input bertipe apa pun. - Baris 6 (
switch v := nilai.(type)): type guardnilai.(type)memeriksa tipe dinamis isi interface. Variabelvotomatis menerima nilai tersebut dengan tipe yang sudah diketahui di tiap cabang. - Baris 7-8 (
case nil:): berjalan ketika interface tidak berisi nilai apa pun. Pada kasus inivbernilainil. - Baris 9-10 (
case int:):vbertipeint, sehingga verb%dpadafmt.Sprintflangsung mencetak angkanya. - Baris 11-12 (
case string:):vbertipestring, sehinggalen(v)menghitung jumlah byte dan verb%qmencetak teks dengan tanda kutip. - Baris 15-16 (
case []int:):vbertipe[]intsehinggalen(v)menghitung jumlah elemennya. Slice dan map dibahas tuntas pada Section Array, Slice, dan Map . - Baris 17-19 (
default:): menampung tipe yang tidak terdaftar. Nilai3.14bertipefloat64; verb%Tmencetak nama tipenya. - Baris 23-28 (
func main): memanggildescribeenam kali dengan enam tipe berbeda; setiap panggilan masuk ke cabang yang berbeda.
case nil, default, dan case Jamak #
Cabang case nil penting untuk data yang tidak selalu terisi: interface kosong memiliki tipe dinamis nil, dan tanpa cabang ini nilai kosong akan jatuh ke default. Sementara default menampung seluruh tipe yang tidak kita daftarkan — tempat terbaik mencatat tipe tak terduga dengan verb %T seperti pada contoh.
Satu case juga boleh memuat beberapa tipe sekaligus, misalnya case int, int64:. Dalam cabang jamak semacam itu kompilator tidak bisa memastikan satu tipe tunggal, sehingga v tetap bertipe interface (di sini any). Operasi khusus perlu konversi eksplisit atau cabang terpisah per tipe.
Type switch juga sering terpakai mengurai data encoding/json; sudut pandang interface dibahas lebih jauh di chapter Type Switch
pada Section Interface. Ketika tipe sudah pasti sejak kompilasi, alur program biasanya berlanjut ke pengulangan — dan untuk itu Go hanya menyediakan satu kata kunci: for, topik chapter berikutnya.
Referensi #
- Spesifikasi Go: Switch statements — bagian akhir mendefinisikan type switch secara resmi.
- Effective Go: interfaces — perilaku nilai interface dan tipe dinamis.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
type-switch-demo.go
Unduh berkaspackage main
import "fmt"
func describe(nilai any) string {
switch v := nilai.(type) {
case nil:
return "nilai kosong (nil)"
case int:
return fmt.Sprintf("bilangan bulat bernilai %d", v)
case string:
return fmt.Sprintf("teks %q sepanjang %d karakter", v, len(v))
case bool:
return fmt.Sprintf("nilai boolean %t", v)
case []int:
return fmt.Sprintf("slice int dengan %d elemen", len(v))
default:
return fmt.Sprintf("tipe %T belum didukung fungsi describe", v)
}
}
func main() {
fmt.Println(describe(42))
fmt.Println(describe("Budi"))
fmt.Println(describe(true))
fmt.Println(describe(nil))
fmt.Println(describe([]int{10, 20, 30}))
fmt.Println(describe(3.14))
}