Reflection

Reflection adalah kemampuan program untuk memeriksa dan memodifikasi strukturnya sendiri saat runtime. Di Go, reflection diimplementasikan melalui package reflect.

Contoh Masalah

Bagaimana cara:

  1. Memeriksa tipe data saat runtime
  2. Mengakses dan memodifikasi struct fields
  3. Membuat fungsi yang bekerja dengan berbagai tipe

Penyelesaian

package main

import (
    "fmt"
    "reflect"
)

// 1. Struct untuk contoh
type Person struct {
    Name    string `json:"name" validate:"required"`
    Age     int    `json:"age" validate:"gte=0,lte=130"`
    Address string `json:"address,omitempty"`
}

// 2. Interface untuk contoh
type Animal interface {
    Speak() string
}

type Dog struct {
    Name string
}

func (d Dog) Speak() string {
    return "Woof!"
}

// 3. Fungsi untuk memeriksa tipe
func inspectType(x interface{}) {
    t := reflect.TypeOf(x)
    v := reflect.ValueOf(x)

    fmt.Printf("Type: %v\n", t)
    fmt.Printf("Kind: %v\n", t.Kind())
    
    if t.Kind() == reflect.Struct {
        fmt.Println("\nFields:")
        for i := 0; i < t.NumField(); i++ {
            field := t.Field(i)
            fmt.Printf("  %s: %v (tag: `%v`)\n",
                field.Name,
                field.Type,
                field.Tag)
        }
    }

    if t.Kind() == reflect.Ptr {
        fmt.Printf("Points to: %v\n", t.Elem())
    }

    fmt.Printf("Value: %v\n", v)
}

// 4. Fungsi untuk memodifikasi nilai
func setValue(x interface{}, fieldName string, value interface{}) error {
    v := reflect.ValueOf(x)
    
    if v.Kind() != reflect.Ptr {
        return fmt.Errorf("x harus pointer")
    }
    
    v = v.Elem()
    if v.Kind() != reflect.Struct {
        return fmt.Errorf("x harus struct")
    }

    field := v.FieldByName(fieldName)
    if !field.IsValid() {
        return fmt.Errorf("field tidak ditemukan: %s", fieldName)
    }

    val := reflect.ValueOf(value)
    if field.Type() != val.Type() {
        return fmt.Errorf("tipe tidak sesuai")
    }

    if !field.CanSet() {
        return fmt.Errorf("field tidak bisa diubah")
    }

    field.Set(val)
    return nil
}

// 5. Fungsi untuk membaca tag
func getStructTags(x interface{}) map[string]string {
    t := reflect.TypeOf(x)
    if t.Kind() != reflect.Struct {
        return nil
    }

    tags := make(map[string]string)
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        tags[field.Name] = string(field.Tag)
    }
    return tags
}

// 6. Fungsi untuk memanggil method
func callMethod(x interface{}, methodName string) ([]reflect.Value, error) {
    v := reflect.ValueOf(x)
    method := v.MethodByName(methodName)
    if !method.IsValid() {
        return nil, fmt.Errorf("method tidak ditemukan: %s", methodName)
    }
    return method.Call(nil), nil
}

// 7. Fungsi untuk membuat instance baru
func createInstance(t reflect.Type) interface{} {
    if t.Kind() == reflect.Ptr {
        t = t.Elem()
    }
    return reflect.New(t).Interface()
}

func main() {
    // Contoh 1: Inspeksi tipe dasar
    fmt.Println("Basic Type Inspection:")
    num := 42
    str := "hello"
    inspectType(num)
    inspectType(str)

    // Contoh 2: Inspeksi struct
    fmt.Printf("\nStruct Inspection:\n")
    p := Person{
        Name:    "John",
        Age:     30,
        Address: "Jakarta",
    }
    inspectType(p)

    // Contoh 3: Modifikasi nilai
    fmt.Printf("\nValue Modification:\n")
    p2 := &Person{Name: "Alice", Age: 25}
    fmt.Printf("Before: %+v\n", p2)
    
    err := setValue(p2, "Age", 26)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    }
    fmt.Printf("After: %+v\n", p2)

    // Contoh 4: Membaca tags
    fmt.Printf("\nStruct Tags:\n")
    tags := getStructTags(Person{})
    for field, tag := range tags {
        fmt.Printf("%s: %s\n", field, tag)
    }

    // Contoh 5: Memanggil method
    fmt.Printf("\nMethod Calling:\n")
    dog := Dog{Name: "Rex"}
    result, err := callMethod(dog, "Speak")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Dog says: %v\n", result[0])
    }

    // Contoh 6: Membuat instance
    fmt.Printf("\nInstance Creation:\n")
    personType := reflect.TypeOf(Person{})
    newPerson := createInstance(personType).(*Person)
    fmt.Printf("New person: %+v\n", newPerson)

    // Contoh 7: Iterasi fields
    fmt.Printf("\nField Iteration:\n")
    v := reflect.ValueOf(p)
    t := v.Type()
    for i := 0; i < v.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)
        fmt.Printf("%s: %v\n", field.Name, value.Interface())
    }
}

Penjelasan Kode

  1. Basic Reflection

    • TypeOf dan ValueOf
    • Kind vs Type
    • Field inspection
  2. Advanced Features

    • Struct tags
    • Method calling
    • Value modification
  3. Common Uses

    • Serialization
    • Validation
    • Generic programming

Output

Basic Type Inspection:
Type: int
Kind: int
Value: 42
Type: string
Kind: string
Value: hello

Struct Inspection:
Type: main.Person
Kind: struct
Fields:
  Name: string (tag: `json:"name" validate:"required"`)
  Age: int (tag: `json:"age" validate:"gte=0,lte=130"`)
  Address: string (tag: `json:"address,omitempty"`)
Value: {John 30 Jakarta}

Value Modification:
Before: &{Name:Alice Age:25 Address:}
After: &{Name:Alice Age:26 Address:}

Struct Tags:
Name: json:"name" validate:"required"
Age: json:"age" validate:"gte=0,lte=130"
Address: json:"address,omitempty"

Method Calling:
Dog says: Woof!

Instance Creation:
New person: &{Name: Age:0 Address:}

Field Iteration:
Name: John
Age: 30
Address: Jakarta

Tips

  • Gunakan reflection dengan bijak
  • Perhatikan performa
  • Validasi tipe dengan benar
  • Handle error dengan baik
  • Dokumentasikan penggunaan reflection