Skip to content

Metode Go

Perbedaan metode dan fungsi di Go adalah metode memiliki receiver sedangkan fungsi tidak dan hanya tipe kustom yang dapat memiliki metode. Mari lihat contoh terlebih dahulu.

go
type IntSlice []int

func (i IntSlice) Get(index int) int {
  return i[index]
}
func (i IntSlice) Set(index, val int) {
  i[index] = val
}

func (i IntSlice) Len() int {
  return len(i)
}

Pertama mendeklarasikan tipe IntSlice tipe dasarnya adalah []int lalu mendeklarasikan tiga metode Get Set dan Len bentuk metode dan fungsi tidak ada perbedaan terlalu besar hanya ada tambahan (i IntSlice) kecil. i adalah receiver IntSlice adalah tipe receiver receiver mirip dengan this atau self di bahasa lain hanya saja di Go perlu secara eksplisit menunjukkannya.

go
func main() {
   var intSlice IntSlice
   intSlice = []int{1, 2, 3, 4, 5}
   fmt.Println(intSlice.Get(0))
   intSlice.Set(0, 2)
   fmt.Println(intSlice)
   fmt.Println(intSlice.Len())
}

Penggunaan metode mirip dengan memanggil metode anggota kelas pertama mendeklarasikan lalu menginisialisasi lalu memanggil.

Receiver Nilai

Receiver juga dibagi menjadi dua jenis receiver nilai dan receiver pointer mari lihat contoh terlebih dahulu

go
type MyInt int

func (i MyInt) Set(val int) {
   i = MyInt(val) // Dimodifikasi tetapi tidak menyebabkan dampak apa pun
}

func main() {
   myInt := MyInt(1)
   myInt.Set(2)
   fmt.Println(myInt)
}

Setelah kode di atas dijalankan akan ditemukan nilai myInt masih 1 tidak dimodifikasi menjadi 2. Metode saat dipanggil akan meneruskan nilai receiver ke dalam metode receiver contoh di atas adalah receiver nilai dapat sederhana dianggap sebagai parameter formal dan memodifikasi nilai parameter formal tidak akan menyebabkan dampak apa pun pada nilai di luar metode lalu bagaimana jika melalui pointer memanggil?

go
func main() {
  myInt := MyInt(1)
  (&myInt).Set(2)
  fmt.Println(myInt)
}

Sayangnya kode seperti ini juga tidak dapat memodifikasi nilai internal untuk dapat mencocokkan tipe receiver Go akan mendereferensikannya dijelaskan sebagai (*(&myInt)).Set(2).

Receiver Pointer

Sedikit dimodifikasi dapat secara normal memodifikasi nilai myInt.

go
type MyInt int

func (i *MyInt) Set(val int) {
   *i = MyInt(val)
}

func main() {
   myInt := MyInt(1)
   myInt.Set(2)
   fmt.Println(myInt)
}

Sekarang receiver adalah receiver pointer meskipun myInt adalah tipe nilai saat memanggil metode receiver pointer melalui tipe nilai Go akan menjelaskannya sebagai (&myint).Set(2). Jadi saat receiver metode adalah pointer tidak peduli apakah pemanggil adalah pointer dapat memodifikasi nilai internal.

Proses passing parameter fungsi adalah copy nilai jika yang diteruskan adalah integer maka copy integer ini jika adalah slice maka copy slice ini tetapi jika adalah pointer hanya perlu copy pointer ini jelas passing pointer lebih sedikit mengonsumsi sumber daya daripada passing slice receiver juga tidak terkecuali receiver nilai dan receiver pointer juga sama prinsipnya. Dalam sebagian besar situasi direkomendasikan menggunakan receiver pointer tetapi keduanya tidak boleh dicampur digunakan baik semuanya digunakan atau tidak digunakan sama sekali lihat contoh di bawah ini.

TIP

Perlu memahami interface terlebih dahulu

go
type Animal interface {
   Run()
}

type Dog struct {
}

func (d *Dog) Run() {
   fmt.Println("Run")
}

func main() {
   var an Animal
   an = Dog{}
   // an = &Dog{} Cara yang benar
   an.Run()
}

Kode ini tidak akan bisa dikompilasi kompiler akan mengeluarkan error sebagai berikut

cannot use Dog{} (value of type Dog) as type Animal in assignment:
  Dog does not implement Animal (Run method has pointer receiver)

Diterjemahkan adalah tidak dapat menggunakan Dog{} untuk menginisialisasi variabel tipe Animal karena Dog tidak mengimplementasikan Animal ada dua solusi untuk menyelesaikannya yang pertama adalah mengubah receiver pointer menjadi receiver nilai yang kedua adalah mengubah Dog{} menjadi &Dog{} selanjutnya dijelaskan satu per satu.

go
type Dog struct {
}

func (d Dog) Run() { // Diubah menjadi receiver nilai
   fmt.Println("Run")
}

func main() { // Dapat berjalan normal
   var an Animal
   an = Dog{}
   // an = &Dog{} Juga dapat
   an.Run()
}

Dalam kode asli receiver metode Run adalah *Dog secara alami yang mengimplementasikan antarmuka Animal adalah pointer Dog bukan struct Dog ini adalah dua tipe yang berbeda jadi kompiler akan认为 Dog{} bukan implementasi Animal oleh karena itu tidak dapat diberikan ke variabel an jadi solusi kedua adalah memberikan pointer Dog ke variabel an. Tetapi saat menggunakan receiver nilai pointer Dog masih dapat secara normal diberikan ke animal ini karena Go akan melakukan dereferensi pada pointer dalam situasi yang sesuai karena melalui pointer dapat menemukan struct Dog tetapi sebaliknya tidak dapat melalui struct Dog menemukan pointer Dog. Jika hanya mencampur receiver nilai dan receiver pointer dalam struct tidak apa-apa tetapi setelah digunakan bersama dengan interface akan muncul error daripada kapan pun baik menggunakan receiver nilai atau semuanya menggunakan receiver pointer membentuk standar yang baik juga dapat mengurangi beban pemeliharaan selanjutnya.

Ada satu situasi lagi yaitu saat receiver nilai dapat dialamati Go akan secara otomatis menyisipkan operator pointer untuk memanggil misalnya slice dapat dialamati masih dapat memodifikasi nilai internalnya melalui receiver nilai. Misalnya kode di bawah ini

go
type Slice []int

func (s Slice) Set(i int, v int) {
  s[i] = v
}

func main() {
  s := make(Slice, 1)
  s.Set(0, 1)
  fmt.Println(s)
}

Output

[1]

Tetapi ini akan menimbulkan masalah lain jika menambahkan elemen padanya situasinya akan berbeda. Lihat contoh di bawah ini

type Slice []int

func (s Slice) Set(i int, v int) {
  s[i] = v
}

func (s Slice) Append(a int) {
  s = append(s, a)
}

func main() {
  s := make(Slice, 1, 2)
  s.Set(0, 1)
  s.Append(2)
  fmt.Println(s)
}
[1]

Outputnya masih sama seperti sebelumnya fungsi append memiliki nilai pengembalian setelah menambahkan elemen ke slice harus menutupi slice asli terutama setelah ekspansi memodifikasi receiver nilai dalam metode tidak akan menghasilkan dampak apa pun ini juga menyebabkan hasil dalam contoh diubah menjadi receiver pointer menjadi normal.

go
type Slice []int

func (s *Slice) Set(i int, v int) {
  (*s)[i] = v
}

func (s *Slice) Append(a int) {
  *s = append(*s, a)
}

func main() {
  s := make(Slice, 1, 2)
  s.Set(0, 1)
  s.Append(2)
  fmt.Println(s)
}

Output

[1 2]

Golang by www.golangdev.cn edit