Go語言入門-方法

Go語言入門-方法

概念

A method is a function with a receiver. A method declaration binds an identifier, the method name, to a method, and associates the method with the receiver’s base type.

方法是一種特殊的函數,綁定一個receiver。 與接收者的基本類型關聯。
基本語法

--官方描述
MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver   = Parameters .
--通俗描述
func([Receiver] ReceiverType) methodName(ParamsList...)(ReturnList...) {
	FunctionBody 
	...
}

其中與函數定義的區別是在名字前面增加了(Receiver] Type)用來標識該方法的接受者的類型。並且 Reciver當在方法中不使用的時候可以不寫

  • 示例1
type yourInt int
//定義一個方法,接收者的類型爲yourInt,使用reciver來表示被綁定的yourInt類型的變量。
func (receiver yourInt) print() {
    println(receiver)
}
//定義一個方法,接收者的類型爲yourInt,也就是隻有yourInt類型纔可以使用改方法,但是該方法中未使用receiver因此可以省略
func (yourInt) print2() {
    println("hello ")
}
func main() {
    your := yourInt(10)
    your.print()
    your.print2()
}
/**
output:
10
hello
*/

其中
1. Receiver-接受者的變量,關聯被綁定的實例的標識符,不推薦使用this、self等,一般使用類型的首字母小寫。當方法內部不引用被綁定的實例,則可以省略接收者變量的標識符。
2. ReceiverType-接收者類型,關聯一個base類型T,而具體的類型可以是T或者T(此處T可以理解go中的引用)。T也不能是接口或指針類型。
3. 方法名-同函數
4. 參數列表-同函數
5. 返回值列表-同函數

使用

基本的使用(T)

示例2-一個點的類型point的基本使用

//定義一個點的類型point
type point struct {
    x, y int
}

//打印綁定變量本身
func (p point) printSelf() {
    fmt.Printf("[%p]->[%d, %d]\n", &p, p.x, p.y)
}

//打印傳入的變量類型
func (point) print(p point) {
    fmt.Printf("[%p]->[%d, %d]\n", &p, p.x, p.y)
}
//打印座標X
func (p point) printX() {
    fmt.Printf("[%p]->[%d, -]\n", &p, p.x)
}

//打印座標Y
func (p point) printY() {
    fmt.Printf("[%p]->[-, %d]\n", &p, p.x)
}
//獲取座標X的值
func (p point) GetX() int {
    return p.x
}

//獲取座標Y的值
func (p point) GetY() int {
    return p.y
}

//座標相加
func (p point) add(p2 point) {
    p.x += p2.x
    p.y += p2.y
}
//座標相減
func (p point) sub(p2 point) {
    p.x -= p2.x
    p.y -= p2.y
}

func main() {
    sour := point{
        x: 1,
        y: 2,
    }
    other := point{
        x: 4,
        y: 3,
    }
    //打印自身
    sour.printSelf()
    //打印入參
    sour.print(other)
    //other變量自己打印
    other.printSelf()
    //打印X座標
    sour.printX()
    //獲取Y座標值
    fmt.Println(sour.GetY())
    //兩座標相加
    sour.add(other)
    //打印相加的結果
    sour.printSelf()
    //兩座標相減
    sour.sub(other)
    //打印相減結果
    sour.printSelf()
}
/**
output:
[0xc00000a0f0]->[1, 2]
[0xc00000a130]->[4, 3]
[0xc00000a150]->[4, 3]
[0xc00000a170]->[1, -]
2
[0xc00000a190]->[1, 2]
[0xc00000a1b0]->[1, 2]
 */

以上示例中奇怪的是即使對 sour進行加減操作,但是sour的值始終不發生變化。以及每次打印的過程中地址都不一致。--------------------是什麼原因呢?

  1. 方法的接收者的base Type爲T,接收者類型是T則表示結構體變量做綁定的接收者是值傳遞,值傳遞不會修改接收者的內容,修改的是接收者的copy的變量。
  2. 方法的接收者的base Type爲T,接收者類型是(*T) 表示使用的是綁定變量的地址,傳遞地址的時候也只值傳遞,但是地址所關聯的內存確是接收者變量的內存,因此可以修改接收者的值。

以上用法爲 值接收者用法。

指針接收者(*T)

  • 示例3 - 指針接收者
//定義一個點的類型point
type point struct {
    x, y int
}

//打印綁定變量本身
func (p *point) printSelf() {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}

//打印傳入的變量類型
func (*point) print(p *point) {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}
//打印座標X
func (p *point) printX() {
    fmt.Printf("[%p]->[%d, -]\n", p, p.x)
}

//打印座標Y
func (p *point) printY() {
    fmt.Printf("[%p]->[-, %d]\n", p, p.x)
}
//獲取座標X的值
func (p *point) GetX() int {
    return p.x
}

//獲取座標Y的值
func (p *point) GetY() int {
    return p.y
}

//座標相加
func (p *point) add(p2 point) {
    p.x += p2.x
    p.y += p2.y
}
//座標相減
func (p *point) sub(p2 *point) {
    p.x -= p2.x
    p.y -= p2.y
}

func main() {
    sour := point{
        x: 1,
        y: 2,
    }
    other := point{
        x: 4,
        y: 3,
    }
    //打印自身
    sour.printSelf()
    //打印入參
    sour.print(&other)
    //other變量自己打印
    other.printSelf()
    //打印X座標
    sour.printX()
    //獲取Y座標值
    fmt.Println(sour.GetY())
    //兩座標相加
    sour.add(other)
    //打印相加的結果
    sour.printSelf()
    //兩座標相減-相減兩次
    sour.sub(&other)
    sour.sub(&other)
    //打印相減結果
    sour.printSelf()
}
/**
output:
[0xc00000a0f0]->[1, 2]
[0xc00000a100]->[4, 3]
[0xc00000a100]->[4, 3]
[0xc00000a0f0]->[1, -]
2
[0xc00000a0f0]->[5, 5]
[0xc00000a0f0]->[-3, -1]
*/

以上通過使用指針接收者實現的方法中,可以看到sour的地址爲[0xc00000a0f0]other的地址爲[0xc00000a100] 使用指針接收者以後地址不再發生變化。

如何選擇接收者類型

  1. 如果要修改實例,用*T
  2. 不修改實例,且實例尺寸小用T,也可以用*T
  3. 大尺寸示例用*T
  4. 包含 同步字段Mutex等,使用*T
  5. 引用類型、字符串、函數等指針包裝對對象,建議用T
  6. 其他使用 *T
    以上來自《Go語言學習筆記》

語法糖:實例或者是實例地址調用方法

可以使用 【實例+“.”+方法】進行方法調用,也可以使用【實例地址+“.”+方法】

  • 示例4-實例地址調用T方法
//定義一個點的類型point
type point struct {
    x, y int
}
//打印綁定變量本身
func (p point) printSelf() {
    fmt.Printf("[%p]->[%d, %d]\n", &p, p.x, p.y)
}

func main() {
    sour := point{
        x: 1,
        y: 2,
    }
    ptr := &sour
    fmt.Printf("ptr = [%p]\n", ptr)
    ptr.printSelf()
    ptr.printSelf()
    sour.printSelf()
    sour.printSelf()
}
/**
ptr = [0xc000072090]
[0xc0000720c0]->[1, 2]
[0xc0000720e0]->[1, 2]
[0xc000072100]->[1, 2]
[0xc000072120]->[1, 2]
 */

通過以上例子中:可以看出receiver是value-值類型, 但是可以傳入實例的地址進行調用,ptr是實例sour的地址。之前也提到過這種方式是一種語法糖,實際上調用過程會轉換爲(*ptr).printSelf()。那是不是調用是通過ptr傳入的地址,意味着傳遞進去的是實例sour的地址嗎?答案是否定的,兩次調用ptr.printSelf()打印出的地址分別是[0xc0000720c0][0xc0000720e0]而ptr的值是[0xc000072090]。那麼可以得出以下結論:

** 實例的地址或者實例都可以調用方法。通過指針調用實際是一個語法糖,對於值接收者方法編譯器會轉換指針ptr.method爲**(*ptr).method.

  • 示例5-實例地址調用*T方法

func main() {
    sour := point{
        x: 1,
        y: 2,
    }
    ptr := &sour
    fmt.Printf("ptr = [%p]\n", ptr)
    //不管是實例地址還是實例去調用 地址均爲發生變化。
    ptr.printSelf2()
    ptr.printSelf2()
    sour.printSelf2()
    sour.printSelf2()
    //發生值值傳遞地址改變
    ptr.printSelf()
}
/**
ptr = [0xc00000a0f0]
[0xc00000a0f0]->[1, 2]
[0xc00000a0f0]->[1, 2]
[0xc00000a0f0]->[1, 2]
[0xc00000a0f0]->[1, 2]
[0xc00000a150]->[1, 2]
 */

通過以上例子中:可以看出receiver是-poinit類型, 但是可以傳入實例進行調用如sour.printSelf2()。之前也提到過這種方式是一種語法糖,實際上調用過程會轉換爲(&sour).printSelf2()。那是不是調用是通過sour傳入實例,意味着傳遞進去的是實例sour的副本嗎?答案是否定的,兩次調用ptr.printSelf2()打印出的地址分別是[0xc00000a0f0][0xc00000a0f0]而ptr的值也是[0xc00000a0f0]。那麼可以得出以下結論:

實例的地址或者實例都可以調用方法。通過指針調用實際是一個語法糖,對於指針接收者防範編譯器會轉換指針sour.method爲(&sour).method.

整理總結:

1. 實例的地址或者實例都可以調用方法。通過指針調用實際是一個語法糖,對於值接收者方法編譯器會轉換指針ptr.method爲(*ptr).method.
2. 實例的地址或者實例都可以調用方法。通過指針調用實際是一個語法糖,對於指針接收者防範編譯器會轉換指針sour.method爲(&sour).method.
3. 方法對實例使用值傳遞還是引用傳遞取決於方法的定義,而不是通過實例和實例地址調用方法的方式區別。

匿名字段的方法

之前在結構體中可以訪問匿名字段的成員,在go語言中也可以使用匿名字段的方法。

  • 示例6-結構體基本的組合
//定義一個點的類型point
type point struct {
    x, y int
}

//打印綁定變量本身
func (p *point) printSelf() {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}
//打印座標X
func (p *point) printX() {
    fmt.Printf("[%p]->[%d, -]\n", p, p.x)
}

//打印傳入的變量類型
func (*point) print(p *point) {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}

type line struct {
    start point
    end point
}
func main() {
    l := line{
    start: point{0, 0},
    end:   point{1, 1},
    }
    fmt.Println(l)
    //調用組合的類型方法
    l.start.printSelf()
    //end的print方法傳入l.start地址當做入參
    l.end.print(&l.start)
    l.end.printSelf()
}
/**
output:
{{0 0} {1 1}}
[0xc000074140]->[0, 0]
[0xc000074140]->[0, 0]
[0xc000074150]->[1, 1]
 */

以上例子中該有 start和end兩個成員變量,可以使用【實例變量名】+ “.” +【成員變量名】+"." + 【方法】這種方式訪問成員變量方法。

  • 示例7-結構體 匿名字段
    我們可以改造以上的例子可以省略調用 start的成員變量名稱。
//定義一個點的類型point
type point struct {
    x, y int
}

//打印綁定變量本身
func (p *point) printSelf() {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}
//打印座標X
func (p *point) printX() {
    fmt.Printf("[%p]->[%d, -]\n", p, p.x)
}

//打印傳入的變量類型
func (*point) print(p *point) {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}

type line struct {
    start point
    end point
}
type line2 struct {
    point
    end point
}

func main() {
    l := line2{
        point: point{0, 0},
        end:   point{2, 2},
    }
    fmt.Println(l)
    //通過匿名變量類型值訪問匿名變量方法
    l.point.printSelf()
    //可以直接使用匿名變量的方法
    l.printSelf()
    //命名變量顯示調用
    l.end.printSelf()
}
/**
output:
{{0 0} {2 2}}
[0xc000070140]->[0, 0]
[0xc000070140]->[0, 0]
[0xc000070150]->[2, 2]
 */

以上例子中 存在匿名變量point,可以使用【實例變量名】+ “.” +【匿名變量類型】+"." + 【方法】這種方式訪問成員變量方法也可以直接通過【實例變量名】+"." + 【方法】進行調用。

  • 示例8-當匿名成員的方法不滿足已經組合的結構體時,通過結構體同名遮蔽的特性,實現覆蓋(override)的邏輯
//定義一個點的類型point
type point struct {
    x, y int
}

//打印綁定變量本身
func (p *point) printSelf() {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}
//打印座標X
func (p *point) printX() {
    fmt.Printf("[%p]->[%d, -]\n", p, p.x)
}

//打印傳入的變量類型
func (*point) print(p *point) {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}

type line struct {
    start point
    end point
}
type line2 struct {
    point
    end point
}
type line3 struct {
    point
    end point
}
func (l *line3) printSelf()  {
    fmt.Printf("[%p]->[start:[%d, %d],end:[%d, %d]]\n", l, l.x, l.y, l.end.x, l.end.y)
}
func main() {
    l := line3{
        point: point{0, 0},
        end:   point{2, 2},
    }
    fmt.Println(l)
    //通過同名遮蓋,就訪問不到匿名變量的方法,而調用新增加的方法printSelf
    l.printSelf()
    
    //通過匿名變量類型值訪問匿名變量方法
    l.point.printSelf()
    //命名變量顯示調用
    l.end.printSelf()
}
/**
{{0 0} {2 2}}
[0xc000012320]->[start:[0, 0],end:[2, 2]]
[0xc000012320]->[0, 0]
[0xc000012330]->[2, 2]
 */

通過結構體同名遮蔽的特性,可以實現覆蓋(override)的邏輯

方法集

什麼是方法集

A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T). Further rules apply to structs containing embedded fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique non-blank method name.

一句話說明就是:每一個類型都關聯一個對應的方法集,接口的方法集是接口本身。其中:
類型T的方法集包含了所有receiverT方法。
類型 *T 方法集包含所有receiver T + *T 方法。

獲取方法集

  • 示例9-通過反射獲取方法集
//定義一個點的類型point
type pointE struct {
    x, y int
}
//打印綁定變量本身
func (p pointE) printSelf() {
    fmt.Printf("[%p]->[%d, %d]\n", &p, p.x, p.y)
}
func (p *pointE) printSelf2() {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}
//打印傳入的變量類型
func (pointE) print(p pointE) {
    fmt.Printf("[%p]->[%d, %d]\n", &p, p.x, p.y)
}
//打印座標X
func (p pointE) printX() {
    fmt.Printf("[%p]->[%d, -]\n", &p, p.x)
}
//打印座標X
func (p *pointE) printX2() {
    fmt.Printf("[%p]->[%d, -]\n", &p, p.x)
}

func GetMethodSet(a interface{})  {
    tp := reflect.TypeOf(a)
    n := tp.NumMethod()
    fmt.Println(n)
    for i := 0 ; i < n; i++ {
        m := tp.Method(i)
        fmt.Println(m.Name, m.Type)
    }
}
func main() {
    var p pointE
    GetMethodSet(p)
}
/**
output:
0
 */

奇怪爲什麼沒有打印出來方法集呢 ?

重點:通過NumMethod和Method只能拿到公有方法也就是方法名是大寫的。

  • 示例10-通過反射獲取方法集-改造版本
//定義一個點的類型point
type pointEE struct {
    x, y int
}
//打印綁定變量本身
func (p pointEE) PrintSelf() {
    fmt.Printf("[%p]->[%d, %d]\n", &p, p.x, p.y)
}
func (p *pointEE) PrintSelf2() {
    fmt.Printf("[%p]->[%d, %d]\n", p, p.x, p.y)
}
//打印傳入的變量類型
func (pointEE) Print(p pointEE) {
    fmt.Printf("[%p]->[%d, %d]\n", &p, p.x, p.y)
}
//打印座標X
func (p pointEE) PrintX() {
    fmt.Printf("[%p]->[%d, -]\n", &p, p.x)
}
//打印座標X
func (p *pointEE) PrintX2() {
    fmt.Printf("[%p]->[%d, -]\n", &p, p.x)
}

func GetMethodSet(a interface{})  {
    tp := reflect.TypeOf(a)
    n := tp.NumMethod()
    fmt.Println(n)
    for i := 0 ; i < n; i++ {
        m := tp.Method(i)
        fmt.Println(m.Name, m.Type)
    }
}
func main() {
    var p pointEE
    GetMethodSet(p)
    fmt.Println("******************")
    var ptr = &p
    GetMethodSet(ptr)
}

/**
output:
3
Print func(main.pointEE, main.pointEE)
PrintSelf func(main.pointEE)
PrintX func(main.pointEE)
******************
5
Print func(*main.pointEE, main.pointEE)
PrintSelf func(*main.pointEE)
PrintSelf2 func(*main.pointEE)
PrintX func(*main.pointEE)
PrintX2 func(*main.pointEE)
 */

可以看出把函數名調整爲大寫就能通過反射出來具體的函數集合,通過以上結果我們可以得出來:

類型T的方法集包含了所有receiverT方法。
類型 *T 方法集包含所有receiver T + *T 方法。
通過反射只能拿到對應的公用方法


方法集作用是什麼?

方法集的作用指針只作用於接口的實現和表達式的轉換,從上面的例子我們可以得出通過實例或者實例指針可以獲得不同的房集合。說通俗一點,當使用實例指針訪問方法時能夠使用很多的方法。當該實例被組合到結構體中也是同樣的情況,大那是接口提中存在匿名字段。這時候方法集看上去具備的繼承的特性。
來個例子

  • 示例11-實現匿名字段的方法集“繼承”。
//定義類型lineEE 兩個點一個長度
type lineEE struct {
    pointEE
    end pointEE
    len int
    
}
//定義一個pointEX 是對pointEE的封裝
type pointEX struct {
    *pointEE
    name string
}

//增加長度賦值的方法
func (l *lineEE) SetLen(len1 int)  {
    l.len = len1
}
//增加獲取長度值的方法
func (l lineEE) GetLen() int {
    return l.len
}
func GetMethodSet(a interface{})  {
    tp := reflect.TypeOf(a)
    n := tp.NumMethod()
    fmt.Println(n)
    for i := 0 ; i < n; i++ {
        m := tp.Method(i)
        fmt.Println(m.Name, m.Type)
    }
}

func main() {
    var p pointEE
    //打印遍歷pointEE值方法
    GetMethodSet(p)
    fmt.Println("******************")
    var ptr = &p
    //打印遍歷pointEE指針和值方法
    GetMethodSet(ptr)
    fmt.Println("******************")
    var l lineEE
    // 打印遍歷lineEE值方法 含:line的值方法。匿名字段pointEE的值方法
    GetMethodSet(l)
    fmt.Println("******************")
    // 打印遍歷lineEE指針方法 含:line的指針方法。匿名字段pointEE的值方法 + 匿名字段的指針方法
    GetMethodSet(&l)
    fmt.Println("******************")
    var pex pointEX
    //打印遍歷pointEx的方法值,因爲是匿名字段pointEE指針,因此包含了值方法和指針方法。
    GetMethodSet(pex)
}
/**
output:
3
Print func(main.pointEE, main.pointEE)
PrintSelf func(main.pointEE)
PrintX func(main.pointEE)
******************
5
Print func(*main.pointEE, main.pointEE)
PrintSelf func(*main.pointEE)
PrintSelf2 func(*main.pointEE)
PrintX func(*main.pointEE)
PrintX2 func(*main.pointEE)
******************
4
GetLen func(main.lineEE) int
Print func(main.lineEE, main.pointEE)
PrintSelf func(main.lineEE)
PrintX func(main.lineEE)
******************
7
GetLen func(*main.lineEE) int
Print func(*main.lineEE, main.pointEE)
PrintSelf func(*main.lineEE)
PrintSelf2 func(*main.lineEE)
PrintX func(*main.lineEE)
PrintX2 func(*main.lineEE)
SetLen func(*main.lineEE, int)
******************
5
Print func(main.pointEX, main.pointEE)
PrintSelf func(main.pointEX)
PrintSelf2 func(main.pointEX)
PrintX func(main.pointEX)
PrintX2 func(main.pointEX)
*/

從以上例子中我們也驗證了方法集的匿名嵌套的幾個特性。

匿名嵌套S,T方法集包含全部的值方法(receiver S)
匿名嵌套*S,T方法集包含全部的值方法(receiver S)+ 指針方法(receiver *S)
匿名嵌套S或者*S,*T方法集包含所有的(receiver S)+ 指針方法(receiver *S)

方法集"繼承" 和使用的特性

類型T的方法集包含了所有receiverT方法。
類型 *T 方法集包含所有receiver T + *T 方法。
匿名嵌套S,T方法集包含全部的值方法(receiver S)
匿名嵌套*S,T方法集包含全部的值方法(receiver S)+ 指針方法(receiver *S)
匿名嵌套S或者*S,*T方法集包含所有的(receiver S)+ 指針方法(receiver *S)

方法表達式

方法和函數一樣,不僅支持直接調用,還可以賦值給變量或作爲函數傳遞。存在值傳遞或者引用傳遞不同的方式。Go語言分爲兩種。分別是method expression和method value。

基本的語法

//method value
instance.Method(args....)
// method expression
type.func(instance, args....)


method value

  • 示例12-method value演示
type point struct {
    x, y int
}

func (p *point) Set(x, y int) {
    p.x = x
    p.y = y
    fmt.Printf("Addr=[%p] Set:x=[%d] y=[%d]\n", p, p.x, p.y)
}
func (p point) Get() (x, y int) {
    fmt.Printf("Addr=[%p] Get:x=[%d] y=[%d]\n", &p, p.x, p.y)
    return p.x, p.y
}
func main() {
    p := point{
        x: 0,
        y: 0,
    }
    p.Set(1,1)
    p.Get()
    //method value
    fmt.Println("method value ")
    fValueSet := p.Set
    fValueGet := p.Get
    fValueSet(3, 3)
    fValueGet()
}
/**
output:
Addr=[0xc00000a0f0] Set:x=[1] y=[1]
Addr=[0xc00000a120] Get:x=[1] y=[1]
method value
Addr=[0xc00000a0f0] Set:x=[3] y=[3]
Addr=[0xc00000a150] Get:x=[1] y=[1]
 */

method value隱式綁定了實參。

  1. 當方法是值方法時,通過變量保存方法時,實際上會重新開闢一塊內存存放賦值時已經綁定變量的副本。
  2. 當方法是指針方法時,通過變量保存方法是,只會開闢一塊內存存放該實例的地址。從而實現看上去是引用的方式。
  • 示例13-method value經典示例
type N int

func (n N) test() {
    fmt.Printf("test.n -> addr=[%p], %v\n", &n, n)
}

func (n *N) testP() {
    fmt.Printf("test.n -> addr=[%p], %d\n", n, *n)
}
func main() {
    n := N(1)
    p := &n
    n++
    f1 := p.test //f1綁定的是當前p所指向的副本,當前p的值爲2
    fp1 := n.testP //fp1綁定的是當前的n 指針方法
    n++
    f2 := n.test //f2綁定的是當前n的副本,當前n的值爲3
    fp2 := p.testP //fp2綁定的是當前的p所指向的n 指針方法
    n++
    f3 := p.test //f3綁定的是當前p所指向的副本,當前n的值爲4
    fp3 := n.testP //fp3綁定的是當前的n 指針方法 
    fmt.Printf("main.n -> addr=[%p], %v\n\n", &n, n)
    //值方法綁定後調用,值方法會生成賦值時狀態下的n的副本
    f1()
    f2()
    f3()
    //指針方法所關聯的地址是n也就是p所指向的內存,
    //由於該內存的值以及改變了,所以指針方法調用打印的是地址的當前值
    fp1()
    fp2()
    fp3()
}
/**
output:
main.n -> addr=[0xc00000a0d8], 4
test.n -> addr=[0xc00000a0f8], 2
test.n -> addr=[0xc00000a128], 3
test.n -> addr=[0xc00000a138], 4
test.n -> addr=[0xc00000a0d8], 4
test.n -> addr=[0xc00000a0d8], 4
test.n -> addr=[0xc00000a0d8], 4
 */

method expresion

通過method expression,方法會被還原爲普通函數的,receiver就會作爲第一個個參數。在調用的時候需要顯示的傳參數。而類型可以是 T或者T。在方法集的介紹中T 和T包含的方法集是不一樣的。這點需要注意。

type N int

func (n N) test() {
    fmt.Printf("test.n -> addr=[%p], %v\n", &n, n)
}

func (n *N) testP() {
    fmt.Printf("test.n -> addr=[%p], %d\n", n, *n)
}
func main() {
    n := N(1)
    fmt.Printf("main.n -> addr=[%p], %v\n\n", &n, n)
    n++
    f1 := N.test //f1是還原的普通函數func(main.N)  參數類型是N
    //invalid method expression N.testP (needs pointer receiver: (*N).testP)
    //fp1 := N.testP //fp1是還原的普通函數func(* main.N)  參數類型是 *N
    fp1 := (*N).testP //fp1是還原的普通函數func(* main.N)  參數類型是 *N
    fmt.Printf("%T\n", f1)
    fmt.Printf("%T\n", fp1)
    //expression不同綁定實例值
    f1(n)
    fp1(&n)
}
/**
output:
main.n -> addr=[0xc00000a0d8], 1

func(main.N)
func(*main.N)
test.n -> addr=[0xc00000a0f8], 2 ---和 addr=[0xc00000a0d8]不一致
test.n -> addr=[0xc00000a0d8], 2
 */

method expression就是把方法集進行還原爲函數,receiver是T入參就是T, receiver是T入參就是T,編譯器也不會進行轉換。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章