Kodeloka
  1. Kodeloka
  2. Kelas Golang
  3. Materi

Domain 4 · 8 bab

Golang Generics

81. Pengenalan Generics & Type Parameters

Memahami konsep Generics di Go 1.18+ untuk membuat kode yang re-usable tanpa mengorbankan Type Safety.

Apa itu Generics?

Generics (diperkenalkan pada Go 1.18) memungkinkan kita menulis fungsi dan tipe data yang bekerja dengan berbagai macam tipe data tanpa harus menduplikasi kode atau menggunakan interface{} (any) yang mengorbankan type safety saat kompilasi.

💡 Sebelum vs Sesudah Generics:
  • Sebelum Go 1.18: Harus buat `SumInt(a, b int)` dan `SumFloat(a, b float64)` terpisah, atau pakai `interface{}` dengan tipe assertion yang rentan runtime panic.
  • Sesudah Go 1.18: Buat 1 fungsi generik `Sum[T Number](a, b T)` yang aman dan efisien!

Sintaks Dasar Type Parameter:

func FunctionName[T Constraint](param T) T { ... }

Contoh program

package main

import (
	"fmt"
)

// Fungsi Generik Sederhana dengan constraint 'any'
func CetakData[T any](val T) {
	fmt.Printf("Value: %v | Tipe: %T\n", val, val)
}

func main() {
	fmt.Println("=== DEMO GOLANG GENERICS ===")
	CetakData[string]("Halo Golang Generics!")
	CetakData[int](2026)
	CetakData[bool](true)
}

Praktik dan kuis bab ini di kelas interaktif →

82. Generic Functions & Type Inference

Membuat fungsi generik dan memahami bagaimana kompilator Go secara otomatis menginferensi tipe data parameter.

Type Inference pada Generic Functions

Saat memanggil fungsi generik, kita tidak selalu wajib menuliskan parameter tipe secara eksplisit seperti Sum[int](10, 20). Kompilator Go cukup cerdas untuk secara otomatis mendeteksi tipe data dari argumen yang diberikan (Type Inference).

📌 Contoh Perbandingan Pemanggilan:
// Pemanggilan Eksplisit:
result := Add[int](5, 10)

// Pemanggilan dengan Type Inference (Dianjurkan):
result := Add(5, 10) // Kompilator otomatis tahu T adalah int

Contoh program

package main

import (
	"fmt"
)

// Fungsi generik mengembalikan elemen pertama slice
func GetFirst[T any](slice []T) T {
	if len(slice) == 0 {
		var zero T
		return zero
	}
	return slice[0]
}

func main() {
	numbers := []int{10, 20, 30}
	names := []string{"Eko", "Budi", "Joko"}

	// Menggunakan Type Inference otomatis
	fmt.Println("First number:", GetFirst(numbers)) // T di-infer sebagai int
	fmt.Println("First name:", GetFirst(names))     // T di-infer sebagai string
}

Praktik dan kuis bab ini di kelas interaktif →

83. Generic Structs & Data Structures

Membangun struktur data generik yang fleksibel seperti Generic Response, Stack, dan Queue.

Generic Structs

Selain fungsi, struct juga dapat dideklarasikan menggunakan parameter tipe. Fitur ini sangat populer dalam pembuatan standar response API atau struktur data umum (Stack, Queue, Linked List).

💡 Contoh Struct Generic Response:
type Response[T any] struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Data    T      `json:"data"`
}

Dengan struct generik di atas, Data dapat diisi dengan struct `User`, slice `[]Product`, atau tipe data apa pun tanpa perlu interface{}.

Contoh program

package main

import (
	"fmt"
)

// Struct Generic Data Response
type Response[T any] struct {
	Status int
	Data   T
}

type User struct {
	Name string
}

func main() {
	// Response dengan data bertipe User
	userRes := Response[User]{
		Status: 200,
		Data:   User{Name: "Eko Khannedy"},
	}

	// Response dengan data bertipe slice string
	listRes := Response[[]string]{
		Status: 200,
		Data:   []string{"Go", "Java", "Python"},
	}

	fmt.Println("User Response:", userRes.Data.Name)
	fmt.Println("List Response:", listRes.Data)
}

Praktik dan kuis bab ini di kelas interaktif →

84. Type Sets & Custom Constraints

Membatasi opsi tipe data generik menggunakan Type Sets interface dengan operator Union (|).

Constraint Tipe Data (Type Sets)

Kadang kita ingin membatasi bahwa fungsi generik hanya boleh menerima angka (misalnya `int`, `float64`), bukan `string` atau `bool`. Di Go, ini dilakukan dengan membuat Interface Constraint menggunakan operator union (|).

📌 Mendefinisikan Custom Constraint:
type Number interface {
    int | int32 | int64 | float32 | float64
}

func Sum[T Number](a, b T) T {
    return a + b
}

Contoh program

package main

import (
	"fmt"
)

// Custom Type Set Constraint
type Number interface {
	int | float64
}

func Penjumlahan[T Number](a, b T) T {
	return a + b
}

func main() {
	fmt.Println("Penjumlahan Int:", Penjumlahan(10, 20))
	fmt.Println("Penjumlahan Float:", Penjumlahan(5.5, 4.3))
	// Penjumlahan("a", "b") // ERROR saat kompilasi! string bukan bagian dari Number
}

Praktik dan kuis bab ini di kelas interaktif →

85. Comparable Constraint & Map Keys

Memahami constraint bawaan `comparable` untuk tipe data yang bisa dibandingkan dengan `==` dan `!=`.

Built-in Constraint: comparable

Go menyediakan constraint bawaan bernama comparable. Constraint ini mencakup semua tipe data yang mendukung operator perbandingan sama dengan (==) dan tidak sama dengan (!=), seperti boolean, angka, string, pointer, channel, dan struct yang komparabel.

💡 Kegunaan Utama comparable:
  • Digunakan saat mencari posisi elemen di slice/array.
  • Digunakan sebagai constraint tipe data Key pada Map (karena Key map di Go wajib bisa dibandingkan).

Contoh program

package main

import (
	"fmt"
)

// Fungsi pencarian index elemen dalam slice
func FindIndex[T comparable](slice []T, target T) int {
	for i, item := range slice {
		if item == target { // Membutuhkan constraint 'comparable'
			return i
		}
	}
	return -1
}

func main() {
	names := []string{"Eko", "Budi", "Joko"}
	fmt.Println("Index 'Budi':", FindIndex(names, "Budi"))
	fmt.Println("Index 'Andi':", FindIndex(names, "Andi"))
}

Praktik dan kuis bab ini di kelas interaktif →

86. Type Approximation Operator (~)

Menggunakan tilde (~) agar constraint generik mendukung custom type alias (underlying type).

Operator Approximation (~)

Jika kita membuat custom alias type seperti type Age int, variabel bertipe Age secara default tidak memenuhi constraint int biasa.

Untuk mengizinkan semua tipe data yang memiliki underlying type sama, kita menggunakan operator tilde (~) di depan nama tipe pada interface constraint.

📌 Penggunaan Tilde (~):
type IntConstraint interface {
    ~int // Mendukung int biasa DAN custom type seperti 'type Age int'
}

Contoh program

package main

import (
	"fmt"
)

type Age int // Custom type dengan underlying type int

type IntegerConstraint interface {
	~int // Menggunakan approximation operator ~
}

func PrintDouble[T IntegerConstraint](val T) {
	fmt.Println("Hasil Double:", val*2)
}

func main() {
	var normalInt int = 25
	var myAge Age = 30

	PrintDouble(normalInt)
	PrintDouble(myAge) // Berhasil karena ada simbol ~int
}

Praktik dan kuis bab ini di kelas interaktif →

87. Generic Interfaces & Method Constraints

Merancang interface generik dan aturan penerapan method pada tipe data generik di Go.

Generic Interfaces

Interface di Go dapat memiliki type parameter. Hal ini berguna untuk membuat abstraksi seperti Getter[T], Repository[T], atau Processor[T].

💡 Contoh Generic Repository Interface:
type Repository[T any] interface {
    Save(entity T) error
    FindById(id string) (T, error)
}
⚠️ Aturan Method pada Struct Generic:

Method dari struct generik tidak boleh mendeklarasikan type parameter baru sendiri; method harus menggunakan type parameter yang dideklarasikan pada struct pemiliknya.

Contoh program

package main

import (
	"fmt"
)

type Getter[T any] interface {
	Get() T
}

type ValueHolder[T any] struct {
	value T
}

func (v ValueHolder[T]) Get() T {
	return v.value
}

func main() {
	var holder Getter[string] = ValueHolder[string]{value: "Data Rahasia"}
	fmt.Println("Isi Holder:", holder.Get())
}

Praktik dan kuis bab ini di kelas interaktif →

88. Package constraints & cmp (Standard Utilities)

Memanfaatkan package bantu standar `cmp` dan `constraints` untuk operasi pembandingan dan pengurutan.

Package Utility Generics

Untuk mempermudah penggunaan Generics, Go menyediakan package standar seperti cmp (mulai Go 1.21) serta eksperimental paket golang.org/x/exp/constraints.

Constraint `cmp.Ordered`:

cmp.Ordered adalah constraint bawaan yang mencakup semua tipe data yang dapat dibandingkan dengan operator order (<, <=, >, >=), yaitu angka dan string.

📌 Fungsi Menghitung Nilai Maksimum (Max):
import "cmp"

func GetMax[T cmp.Ordered](a, b T) T {
    if a > b {
        return a
    }
    return b
}

Contoh program

package main

import (
	"fmt"
)

// Simulasi cmp.Ordered (tipe yang bisa dibandingkan dengan < dan >)
type Ordered interface {
	~int | ~float64 | ~string
}

func Max[T Ordered](a, b T) T {
	if a > b {
		return a
	}
	return b
}

func main() {
	fmt.Println("Max Int (10 vs 25):", Max(10, 25))
	fmt.Println("Max String ('Apple' vs 'Banana'):", Max("Apple", "Banana"))
}

Praktik dan kuis bab ini di kelas interaktif →