BAB 37: Konversi Tipe Data

Pelajari cara mengonversi antar tipe data di Go: konversi numerik eksplisit, konversi string menggunakan package strconv, konversi byte slice, dan type assertion untuk interface.

Di Bab 7, kamu sudah melihat sekilas bagaimana Go menolak konversi tipe implisit — compiler akan menolak float64(25) + int(10) karena keduanya berbeda tipe, meski secara logika penjumlahan itu masuk akal. Pendekatan ini disengaja: Go ingin kamu tahu persis kapan dan bagaimana tipe berubah, bukan membiarkan compiler menebak niatmu.

Tapi penolakan ini bukan tanpa jalan keluar. Go menyediakan mekanisme konversi yang lengkap dan terkontrol — dari konversi antar tipe numerik, konversi string menggunakan package strconv, hingga type assertion untuk mengekstrak nilai konkret dari interface. Bab ini membahas semuanya secara sistematis.

Konversi Antar Tipe Numerik

Konversi antar tipe numerik yang kompatibel dilakukan dengan sintaks TipeTujuan(nilai). Ini adalah konversi paling sederhana dan tidak mengembalikan error.

// konversi.go
package main

import "fmt"

func main() {
    stok := 47         // int
    rasio := 0.85      // float64

    // int ke float64 untuk operasi campuran
    proyeksi := float64(stok) * rasio
    fmt.Printf("proyeksi stok: %.1f\n", proyeksi)

    // float64 ke int — desimal dipotong, bukan dibulatkan
    stokBulat := int(proyeksi)
    fmt.Printf("stok bulat: %d\n", stokBulat)

    // int ke int8 — perhatikan range yang lebih sempit
    var kode int8 = int8(stok)
    fmt.Printf("kode: %d\n", kode)
}
proyeksi stok: 39.9
stok bulat: 39
kode: 47

Konversi dari float64 ke int memotong bagian desimal begitu saja — 39.95 menjadi 39, bukan 40. Ini bukan pembulatan, melainkan truncation.

Konversi ke tipe dengan range lebih kecil bisa mengakibatkan data terpotong tanpa peringatan. int(300) dikonversi ke int8 akan menghasilkan nilai yang salah karena int8 hanya muat sampai 127. Go tidak menghasilkan error — nilainya hanya “membungkus” di sekitar batas range.

Konversi String dan Angka

Konversi antara string dan angka membutuhkan bantuan package strconv. Casting langsung seperti string(42) tidak menghasilkan "42" — ia menghasilkan karakter dengan Unicode code point 42 (yang bukan karakter yang bisa dicetak).

Integer ke String dan Sebaliknya

strconv.Itoa mengonversi int ke string. Kebalikannya, strconv.Atoi, mengonversi string ke int — dan karena proses ini bisa gagal (jika string bukan angka valid), ia mengembalikan dua nilai: hasil dan error.

// konversi.go
package main

import (
    "fmt"
    "strconv"
)

func main() {
    // int ke string
    jumlah := 250
    teks := strconv.Itoa(jumlah)
    fmt.Printf("sebagai string: %q (panjang: %d)\n", teks, len(teks))

    // string ke int
    input := "128"
    angka, err := strconv.Atoi(input)
    if err != nil {
        fmt.Println("gagal konversi:", err)
        return
    }
    fmt.Printf("sebagai int: %d\n", angka)

    // input tidak valid
    _, err = strconv.Atoi("dua ratus")
    if err != nil {
        fmt.Println("error:", err)
    }
}
sebagai string: "250" (panjang: 3)
sebagai int: 128
error: strconv.Atoi: parsing "dua ratus": invalid syntax

Float ke String dan Sebaliknya

Untuk float, tersedia strconv.FormatFloat dan strconv.ParseFloat. Keduanya memberi kontrol atas format dan presisi.

// konversi.go
package main

import (
    "fmt"
    "strconv"
)

func main() {
    harga := 189500.75

    // float64 ke string: format 'f', 2 desimal, bit size 64
    teks := strconv.FormatFloat(harga, 'f', 2, 64)
    fmt.Printf("harga: %s\n", teks)

    // format eksponensial
    notasiE := strconv.FormatFloat(harga, 'e', 3, 64)
    fmt.Printf("notasi E: %s\n", notasiE)

    // string ke float64
    input := "1750.50"
    nilai, err := strconv.ParseFloat(input, 64)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    fmt.Printf("parsed: %.2f\n", nilai)
}
harga: 189500.75
notasi E: 1.895e+05
parsed: 1750.50

Parameter format 'f' menghasilkan notasi desimal biasa, 'e' menghasilkan notasi eksponensial, dan 'g' memilih yang lebih ringkas secara otomatis. Parameter presisi mengontrol jumlah digit setelah titik desimal.

Konversi dengan Basis Bilangan

strconv.ParseInt dan strconv.FormatInt mendukung konversi ke basis selain 10 — berguna saat bekerja dengan data biner, oktal, atau heksadesimal.

// konversi.go
package main

import (
    "fmt"
    "strconv"
)

func main() {
    // desimal ke biner, oktal, hex
    angka := int64(255)
    fmt.Println("biner :", strconv.FormatInt(angka, 2))
    fmt.Println("oktal :", strconv.FormatInt(angka, 8))
    fmt.Println("hex   :", strconv.FormatInt(angka, 16))

    // string biner ke int64
    dariBiner, _ := strconv.ParseInt("11111111", 2, 64)
    fmt.Printf("dari biner: %d\n", dariBiner)

    // string hex ke int64
    dariHex, _ := strconv.ParseInt("ff", 16, 64)
    fmt.Printf("dari hex: %d\n", dariHex)
}
biner : 11111111
oktal : 377
hex   : ff
dari biner: 255
dari hex: 255

Konversi String dan Byte Slice

String di Go pada dasarnya adalah urutan byte yang immutable. Karena itu, konversi antara string dan []byte adalah operasi yang sangat umum — terutama saat bekerja dengan I/O, enkripsi, atau manipulasi data biner.

// konversi.go
package main

import "fmt"

func main() {
    pesan := "Sistem Aktif"

    // string ke []byte
    data := []byte(pesan)
    fmt.Println("bytes:", data)

    // modifikasi byte (string asli tidak terpengaruh)
    data[0] = 's'
    fmt.Println("dimodifikasi:", string(data))
    fmt.Println("asli        :", pesan)

    // []byte ke string
    raw := []byte{71, 111, 108, 97, 110, 103}
    fmt.Println("dari bytes:", string(raw))
}
bytes: [83 105 115 116 101 109 32 65 107 116 105 102]
dimodifikasi: sistem Aktif
asli        : Sistem Aktif

Konversi string ke []byte membuat salinan data, bukan referensi — itulah mengapa mengubah data[0] tidak memengaruhi pesan yang asli.

Ada juga konversi string ke []rune untuk bekerja dengan karakter Unicode. []rune bekerja per karakter, sedangkan []byte bekerja per byte. Untuk teks yang mengandung karakter non-ASCII, gunakan []rune jika kamu perlu mengakses atau memanipulasi per karakter.

Type Assertion untuk Interface

Ketika sebuah nilai disimpan dalam variabel bertipe interface{} (atau any — alias yang ditambahkan sejak Go 1.18), kamu perlu type assertion untuk mengakses nilai konkretnya.

// konversi.go
package main

import "fmt"

func cetakInfo(data any) {
    // type assertion langsung — bisa panic jika tipe tidak sesuai
    teks, ok := data.(string)
    if ok {
        fmt.Printf("string dengan panjang %d: %q\n", len(teks), teks)
        return
    }

    angka, ok := data.(int)
    if ok {
        fmt.Printf("integer: %d\n", angka)
        return
    }

    fmt.Printf("tipe tidak dikenali: %T\n", data)
}

func main() {
    cetakInfo("laporan bulanan")
    cetakInfo(42)
    cetakInfo(3.14)
}
string dengan panjang 15: "laporan bulanan"
integer: 42
tipe tidak dikenali: float64

Pola nilai, ok := data.(Tipe) disebut comma-ok assertion — aman karena tidak akan panic jika tipenya tidak sesuai. Jika ok bernilai false, variabel nilai akan berisi zero value dari Tipe yang diminta.

Type Switch

Untuk menangani banyak kemungkinan tipe sekaligus, type switch jauh lebih bersih daripada serangkaian if dengan comma-ok assertion.

// konversi.go
package main

import "fmt"

type Laporan struct {
    Judul string
}

func deskripsikan(nilai any) string {
    switch v := nilai.(type) {
    case string:
        return fmt.Sprintf("teks %d karakter", len(v))
    case int:
        return fmt.Sprintf("bilangan bulat %d", v)
    case float64:
        return fmt.Sprintf("desimal %.4f", v)
    case bool:
        if v {
            return "kondisi benar"
        }
        return "kondisi salah"
    case Laporan:
        return fmt.Sprintf("laporan: %s", v.Judul)
    case nil:
        return "nilai kosong"
    default:
        return fmt.Sprintf("tipe tidak dikenal: %T", v)
    }
}

func main() {
    nilai := []any{
        "kuartal tiga",
        99,
        98.6,
        true,
        Laporan{Judul: "Rekap Penjualan"},
        nil,
    }

    for _, v := range nilai {
        fmt.Println(deskripsikan(v))
    }
}
teks 11 karakter
bilangan bulat 99
desimal 98.6000
kondisi benar
laporan: Rekap Penjualan
nilai kosong

Dalam type switch, variabel v secara otomatis bertipe konkret di setiap case — di case string, v bertipe string; di case int, v bertipe int. Tidak perlu assertion tambahan.

Latihan

Latihan 1 — Kalkulator tipe campuran: Buat fungsi hitungRata(nilai []string) (float64, error) yang menerima slice string berisi angka (misalnya ["85", "92", "78"]), mengonversi setiap elemen ke float64, dan mengembalikan rata-ratanya. Kembalikan error jika ada elemen yang bukan angka valid.

Latihan 2 — Formatter angka: Tulis fungsi formatAngka(n int) string yang mengonversi integer ke representasi string dalam empat basis: desimal, biner, oktal, dan heksadesimal, lalu mengembalikan keempatnya dalam satu string terformat. Contoh output: "42 | 101010 | 52 | 2a".

Latihan 3 — Prosesor data dinamis: Buat fungsi proses(data []any) map[string]int yang menerima slice bertipe any dan mengembalikan peta jumlah elemen per tipe. Contoh: jika input berisi 3 string, 2 int, dan 1 float64, kembalikan {"string": 3, "int": 2, "float64": 1}. Gunakan type switch untuk mendeteksi tipe setiap elemen.

Konversi tipe adalah salah satu mekanisme yang memperlihatkan filosofi Go paling jelas: eksplisit lebih baik dari implisit. Kamu tahu persis kapan data berubah bentuk, di baris mana, dan apa risikonya. Bersama semua yang sudah kamu pelajari — dari goroutine dan channel hingga standard library dan konversi tipe — kamu kini punya fondasi yang solid untuk mulai membangun program Go yang nyata.

Referensi

  1. 1Package strconv — Go Standard Library Documentation
  2. 2Conversions — The Go Programming Language Specification
  3. 3String Conversion — Go by Example