A.29. Reflect

Reflection adalah teknik untuk inspeksi variabel, mengambil informasi dari variabel tersebut atau bahkan memanipulasinya. Cakupan informasi yang bisa didapatkan lewat reflection sangat luas, seperti melihat struktur variabel, tipe, nilai pointer, dan banyak lagi.

Go menyediakan package reflect, berisikan banyak sekali fungsi untuk keperluan reflection. Pada chapter ini, kita akan belajar tentang dasar penggunaan package tersebut.

Dari banyak fungsi yang tersedia di dalam package tersebut, ada 2 fungsi yang paling penting untuk diketahui, yaitu reflect.ValueOf() dan reflect.TypeOf().

  • Fungsi reflect.ValueOf() akan mengembalikan objek dalam tipe reflect.Value, yang berisikan informasi yang berhubungan dengan nilai pada variabel yang dicari.
  • Sedangkan reflect.TypeOf() mengembalikan objek dalam tipe reflect.Type. Objek tersebut berisikan informasi yang berhubungan dengan tipe data variabel yang dicari.

A.29.1. Mencari Tipe Data & Value Menggunakan Reflect

Dengan reflection, tipe data dan nilai variabel dapat diketahui dengan mudah. Contoh penerapannya bisa dilihat pada kode berikut.

package main

import "fmt"
import "reflect"

func main() {
    var number = 23
    var reflectValue = reflect.ValueOf(number)

    fmt.Println("tipe  variabel :", reflectValue.Type())

    if reflectValue.Kind() == reflect.Int {
        fmt.Println("nilai variabel :", reflectValue.Int())
    }
}

Dasar Pemrograman Golang - Pemanfaatan reflect

Fungsi reflect.valueOf() memiliki parameter yang bisa menampung segala jenis tipe data. Fungsi tersebut mengembalikan objek dalam tipe reflect.Value, yang berisikan informasi mengenai variabel yang bersangkutan.

Objek reflect.Value memiliki beberapa method, salah satunya Type(). Method ini mengembalikan tipe data variabel yang bersangkutan dalam bentuk string.

Statement reflectValue.Int() menghasilkan nilai int dari variabel number. Untuk menampilkan nilai variabel reflect, harus dipastikan dulu tipe datanya. Ketika tipe data adalah int, maka bisa menggunakan method Int(). Ada banyak lagi method milik struct reflect.Value yang bisa digunakan untuk pengambilan nilai dalam bentuk tertentu, contohnya: reflectValue.String() digunakan untuk mengambil nilai string, reflectValue.Float64() untuk nilai float64, dan lainnya.

Perlu diketahui, fungsi yang digunakan harus sesuai dengan tipe data nilai yang ditampung variabel. Jika fungsi yang digunakan berbeda dengan tipe data variabelnya, maka akan menghasilkan error. Contohnya pada variabel menampung nilai bertipe float64, maka tidak bisa menggunakan method String().

Diperlukan adanya pengecekan tipe data nilai yang disimpan, agar pengambilan nilai bisa tepat. Salah satunya bisa dengan cara seperti kode di atas, yaitu dengan mengecek dahulu apa jenis tipe datanya menggunakan method Kind(), setelah itu diambil nilainya dengan method yang sesuai.

List konstanta tipe data dan method yang bisa digunakan dalam refleksi di Go bisa dilihat di https://godoc.org/reflect#Kind

Pengaksesan Nilai Dalam Bentuk interface{}

Jika nilai hanya diperlukan untuk ditampilkan ke output, bisa menggunakan .Interface(). Lewat method tersebut segala jenis nilai bisa diakses dengan mudah.

var number = 23
var reflectValue = reflect.ValueOf(number)

fmt.Println("tipe  variabel :", reflectValue.Type())
fmt.Println("nilai variabel :", reflectValue.Interface())

Fungsi Interface() mengembalikan nilai interface kosong atau interface{}. Nilai aslinya sendiri bisa diakses dengan meng-casting interface kosong tersebut.

var nilai = reflectValue.Interface().(int)

A.29.2. Pengaksesan Informasi Property Variabel Objek

Reflect bisa digunakan untuk mengambil informasi semua property variabel objek cetakan struct, dengan catatan property-property tersebut bermodifier public. Langsung saja kita praktekan, siapkan sebuah struct bernama student.

type student struct {
    Name  string
    Grade int
}

Buat method baru untuk struct tersebut, dengan nama method getPropertyInfo(). Method ini berisikan kode untuk mengambil dan menampilkan informasi tiap property milik struct student.

func (s *student) getPropertyInfo() {
    var reflectValue = reflect.ValueOf(s)

    if reflectValue.Kind() == reflect.Ptr {
        reflectValue = reflectValue.Elem()
    }

    var reflectType = reflectValue.Type()

    for i := 0; i < reflectValue.NumField(); i++ {
        fmt.Println("nama      :", reflectType.Field(i).Name)
        fmt.Println("tipe data :", reflectType.Field(i).Type)
        fmt.Println("nilai     :", reflectValue.Field(i).Interface())
        fmt.Println("")
    }
}

Terakhir, lakukan uji coba method di fungsi main.

func main() {
    var s1 = &student{Name: "wick", Grade: 2}
    s1.getPropertyInfo()
}

Dasar Pemrograman Golang - Pengaksesan property menggunakan reflect

Di dalam method getPropertyInfo terjadi beberapa hal. Pertama objek reflect.Value dari variabel s diambil. Setelah itu dilakukan pengecekan apakah variabel objek tersebut merupakan pointer atau tidak (bisa dilihat dari if reflectValue.Kind() == reflect.Ptr, jika bernilai true maka variabel adalah pointer). jika ternyata variabel memang pointer, maka perlu diambil objek reflect aslinya dengan cara memanggil method Elem().

Setelah itu, dilakukan perulangan sebanyak jumlah property yang ada pada struct student. Method NumField() akan mengembalikan jumlah property publik yang ada dalam struct.

Di tiap perulangan, informasi tiap property struct diambil berurutan dengan lewat method Field(). Method ini ada pada tipe reflect.Value dan reflect.Type.

  • reflectType.Field(i).Name akan mengembalikan nama property
  • reflectType.Field(i).Type mengembalikan tipe data property
  • reflectValue.Field(i).Interface() mengembalikan nilai property dalam bentuk interface{}

Pengambilan informasi property, selain menggunakan indeks, bisa diambil berdasarkan nama field dengan menggunakan method FieldByName().

A.29.3. Pengaksesan Informasi Method Variabel Objek

Informasi mengenai method juga bisa diakses lewat reflect, syaratnya masih sama seperti pada pengaksesan proprerty, yaitu harus bermodifier public.

Pada contoh di bawah ini informasi method SetName akan diambil lewat reflection. Siapkan method baru di struct student, dengan nama SetName.

func (s *student) SetName(name string) {
    s.Name = name
}

Buat contoh penerapannya di fungsi main.

func main() {
    var s1 = &student{Name: "john wick", Grade: 2}
    fmt.Println("nama :", s1.Name)

    var reflectValue = reflect.ValueOf(s1)
    var method = reflectValue.MethodByName("SetName")
    method.Call([]reflect.Value{
        reflect.ValueOf("wick"),
    })

    fmt.Println("nama :", s1.Name)
}

Dasar Pemrograman Golang - Eksekusi method lewat reflection

Pada kode di atas, disiapkan variabel s1 yang merupakan instance struct student. Awalnya property Name variabel tersebut berisikan string "john wick".

Setelah itu, refleksi nilai objek tersebut diambil, refleksi method SetName juga diambil. Pengambilan refleksi method dilakukan menggunakan MethodByName dengan argument adalah nama method yang diinginkan, atau bisa juga lewat indeks method-nya (menggunakan Method(i)).

Setelah refleksi method yang dicari sudah didapatkan, Call() dipanggil untuk eksekusi method.

Jika eksekusi method diikuti pengisian parameter, maka parameternya harus ditulis dalam bentuk array []reflect.Value berurutan sesuai urutan deklarasi parameter-nya. Dan nilai yang dimasukkan ke array tersebut harus dalam bentuk reflect.Value (gunakan reflect.ValueOf() untuk pengambilannya).

[]reflect.Value{
    reflect.ValueOf("wick"),
}