Определение получателя как значения
Формат:
func (r T) Xxx() {}
Может вызывать либо по значению, либо по указателю.
При вызове с указателем значениебудет передан автоматически, (на самом деле он использует *
для получения значения вызывающего и передачи его).
Определение получателя в качестве указателя
Формат:
func (r *T) Xxx() {}
В принципе, должен вызываться только с указателем, но это не обязательно.
Поскольку при вызове со значением вместо указателя компилятор позаботится об этом, когда это возможно:
- Если значение адресуемое, (что верно для большинства типов данных в go) .
Тогда компилятор возьмет адрес (через &
) и передаст его автоматически.
Это позволяет напрямую вызывать метод указателя со значением, (это довольно распространенное явление в go, и это облегчает жизнь программиста) . - Если значение не адресуемое, (что встречается редко, но существует) .
Тогда нужно явно передать адрес,в противном случае вы получите сообщение об ошибке при компиляции.
, например, элемент map
не адресуем.
Tips
Предпочтительнее указатель вызывающей стороны, при определении метода, если это правильно.
Причины:
- Это может изменить вызывающего.
- Это более легкий для сложного вызывающего.
То, что передается методу, зависит от сигнатуры метода, а не от того, как вы его называете (это аналогично параметру) .
- Когда объявляется вызывающий как указатель
(r *T)
, он передает указатель. - Когда объявляется вызывающий как значение
(r T)
, он передает копию исходного звонящего.
T
сам по себе не может быть указателем.
Code
И вот код go, который я написал, изучая эту функцию.
2 функции, вызываемые в main()
, проверяют 2 функции соответственно.
method_learn.go:
// method - test
package main
import (
"fmt"
"math"
)
type Vertex struct {
x float64
y float64
}
// abs, with pointer caller,
func (v *Vertex) AbsPointer() float64 {
return math.Sqrt(v.x*v.x + v.y*v.y)
}
// scale, with pointer caller,
func (v *Vertex) ScalePointer(f float64) *Vertex {
v.x = v.x * f
v.y = v.y * f
return v
}
// abs, with value caller,
func (v Vertex) AbsValue() float64 {
return math.Sqrt(v.x*v.x + v.y*v.y)
}
// test - method with pointer caller,
func pointerCallerLearn() {
vt := Vertex{3, 4}
fmt.Printf("Abs of %v is %v. (Call %s method, with %s)\n", vt, vt.AbsPointer(), "pointer", "value") // call pointer method, with value,
fmt.Printf("Abs of %v is %v. (Call %s method, with %s)\n\n", vt, (&vt).AbsPointer(), "pointer", "pointer") // call pointer method, with pointer,
// scala, change original caller,
fmt.Printf("%v scale by 10 is: %v (Call %s method, with %s)\n", vt, vt.ScalePointer(10), "pointer", "value") // call pointer method, with value,
fmt.Printf("%v scale by 10 is: %v (Call %s method, with %s)\n", vt, (&vt).ScalePointer(10), "pointer", "pointer") // call pointer method, with pointer,
}
// test - method with value caller,
func valueCallerLearn() {
vt := Vertex{3, 4}
fmt.Printf("Abs of %v is %v. (Call %s method, with %s)\n", vt, (&vt).AbsValue(), "value", "pointer") // call value method, with pointer,
fmt.Printf("Abs of %v is %v. (Call %s method, with %s)\n", vt, vt.AbsValue(), "value", "value") // call value method, with value,
}
func main() {
// pointerCallerLearn()
valueCallerLearn()
}
Просто измените main()
и запустите через go run method_test.go
, затем проверьте вывод, чтобы увидеть, как он работает.