先思考一個問題,什么是方法,什么是函數?
方法是從屬于某個結構體或者非結構體的。在func這個關鍵字和方法名中間加了一個特殊的接收器類型,這個接收器可以是結構體類型的或者是非結構體類型的。從屬的結構體獲取該方法。
函數則沒有這種從屬關系。
func (t Type) methodName(parameter list) {
}
type Teacher struct {name stringsalary intcurrency string
}// 在結構體類型上,創建一個方法并調用。
func (tea Teacher) testSalary() {fmt.Printf("Salary of %s is %d %s", tea.name, tea.salary, tea.currency)
}func testUpFun() {tea:= Teacher{name: "malou",salary: 10666,currency: "元",}tea.testSalary()
}
相同的名字的方法可以定義在不同的類型上,而相同名字的函數是不允許的
// Rectangle 定義Rectangle結構體
type Rectangle struct {length intwidth int
}// Circle 定義Circle 結構體
type Circle struct {radius float64
}func (rectangle Rectangle) Area() int {return rectangle.width * rectangle.length
}func (circle Circle) Area() float64 {return math.Pi * circle.radius * circle.radius
}func testArea() {r := Rectangle{width: 10,length: 20,}fmt.Printf("Area is %d\n", r.Area())c := Circle{radius: 12,}fmt.Printf("Area is %f\n", c.Area())
}
值接收器和指針接收器之間的區別在于,在指針接收器的方法內部的改變對于調用者是可見的,然而值接收器的情況不是這樣的。
// Dog struct
type Dog struct {name stringage int
}// 使用值接收器的方法
func (d Dog) changeDogName(newName string) {d.name = newName
}// 使用指針接收器的方法
func (d *Dog) changeAge(newAge int) {d.age = newAge
}func testPointerStruct() {d := Dog{name: "金mao",age: 22,}// 并沒有改變實際的值,只是改變的變量的副本fmt.Printf("before change is %s\n", d.name)d.changeDogName("馬犬")fmt.Printf("after change is %s\n", d.name)// 改變的是變量本身的值fmt.Printf("before change is %d\n", d.age)d.changeAge(11)fmt.Printf("after change is %d\n", d.age)
}
那什么時候使用指針接收器,什么時候使用值接收器?指針接收器可以使用在:對方法內部的接收器所做的改變應該對調用者可見時。當拷貝一個結構體的代價過于昂貴的時候,比如說結構體中有很多字段,如果方法內使用這個結構體做為值接收器需要拷貝整個結構體,這個代價十分昂貴,這種情況下使用指針接收器,結構體不會被拷貝,只會傳遞一個指針到方法的內部。在其他的所有情況,值接收器都可以被使用。在方法中使用值接收器 和 在函數中使用值參數:
type rectangle struct {width intlength int
}
// 函數中的參數,值類型,只能傳遞一個值類型
func area(r rectangle) {fmt.Printf("Area Function result :%d\n", r.length*r.width)
}func (r rectangle) area() {fmt.Printf("Area Method result :%d\n", r.length*r.width)
}func testFunAndMethod() {r := rectangle{width: 10,length: 15,}area(r)r.area()p := &r// (*p).area(),go解釋器會自動的解引用p.area()
}// 在方法中使用指針,和在函數中使用指針參數
func (r *rectangle) perimeter() {fmt.Printf("Area Method result is %d\n", r.width*r.length)
}func perimeter(r *rectangle) {fmt.Printf("Area Function result is %d\n", r.width*r.length)
}func testPointerStruct1() {r := rectangle{width: 12,length: 10,}p := &rperimeter(p)p.perimeter()// r.perimeter() 解釋為 (&r).perimeter() 還有一種是(*p).name 相互解引用,從指針p->(*p),從值r到指針(&r)r.perimeter()
}
小結:
大多數方法都使用的是結構體從屬,注意傳遞的是值傳遞還是指針傳遞。