首頁 > 軟體

go語言方法集為型別新增方法範例解析

2022-04-15 13:01:00

1概述

在物件導向程式設計中,一個物件其實也就是一個簡單的值或者一個變數,在這個物件中會包含一些函數,這種帶有接收者的函數,我們稱為方法(method)。本質上,一個方法則是一個和特殊型別關聯的函數。

一個物件導向的程式會用方法來表達其屬性和對應的操作,這樣使用這個物件的使用者就不需要直接去操作物件,而是藉助方法來做這些事情。

在Go語言中,可以給任意自定義型別(包括內建型別,但不包括指標型別)新增相應的方法。

⽅法總是繫結物件範例,並隱式將範例作為第⼀實參 (receiver),方法的語法如下:

func (receiver ReceiverType) funcName (parameters) (results)
  • 引數 receiver 可任意命名。如⽅法中未曾使⽤,可省略引數名。
  • 引數 receiver 型別可以是 T 或 *T。基本類型 T 不能是接⼝或指標。
  • 不支援過載方法,也就是說,不能定義名字相同但是不同引數的方法。

2為型別新增方法

2.1基礎型別作為接收者

type MyInt int//自定義型別,給int改名為MyInt
//在函數定義時,在其名字之前放上一個變數,即是一個方法
func (a MyInt) Add(b MyInt) MyInt {//物件導向
    return a + b
}
//傳統方式的定義
func Add(a, b MyInt) MyInt {//程式導向
    return a + b
}
func main() {
    var a MyInt=1   // a := MyInt(1)  等價
    var b MyInt=1
//呼叫func (aMyInt) Add(bMyInt)
fmt.Println("a.Add(b)=",a.Add(b))//a.Add(b)=2
//呼叫func Add(a,bMyInt)
fmt.Println("Add(a,b)=",Add(a,b))//Add(a,b)=2
}

通過上面的例子可以看出,物件導向只是換了一種語法形式來表達。方法是函數的語法糖,因為receiver其實就是方法所接收的第1個引數。

注意:雖然方法的名字一模一樣,但是如果接收者不一樣,那麼方法就不一樣。

2.2結構體作為接收者

方法裡面可以存取接收者的欄位,呼叫方法通過點(. )存取,就像struct裡面存取欄位一樣:

package main
import "fmt"
func main(){
	jeff:=user{1,"jeff",18,"上海"}
	fmt.Println(jeff.Add(10))
}
// 相當於定義user類
type user struct {
	id int
	name string
	age int
	addr string
}
// 新增Add方法,接收引數num
func (p user) Add(num int)int{
	fmt.Println(p.age)
	return p.age+num
}

3值語意和參照語意

package main
import "fmt"
func main() {
	//指標作為接收者,參照語意
	jeff := Person{"jeff","男",18}//初始化
	fmt.Println("函數呼叫前=",jeff)//函數呼叫前= {jeff 男 18}
	(&jeff).Add()  // 生效,(&jeff)拿到jeff的地址(指標)
	//jeff.Add()  // 修改不生效
	fmt.Println("函數呼叫後=",jeff)//函數呼叫後= {aaa 女 22}
	fmt.Println("==========================")
	chary := Person{"chary","女",18}//初始化
	//值作為接收者,值語意
	fmt.Println("函數呼叫前=",chary)//函數呼叫前= {chary 女 18}
	chary.Add2()  //不生效
	//(&chary).Add()
	fmt.Println("函數呼叫後=",chary)//函數呼叫後= {chary 女 18}
}
type Person struct {
	name string
	sex string
	age int
}
//指標作為接收者,參照語意
func (p *Person) Add(){
	//給成員賦值
	(*p).name = "aaa"
	p.sex = "女"
	p.age = 22
}
//值作為接收者,值語意
func (p Person) Add2(){
	//給成員賦值
	p.name = "bbb"
	p.sex = "男"
	p.age = 22
}

4方法集

型別的方法集是指可以被該型別的值呼叫的所有方法的集合。

用範例範例 value 和 pointer 呼叫方法(含匿名欄位)不受⽅法集約束,編譯器編總是查詢全部方法,並自動轉換 receiver 實參。

4.1型別 *T 方法集

一個指向自定義型別的值的指標,它的方法集由該型別定義的所有方法組成,無論這些方法接受的是一個值還是一個指標。

如果在指標上呼叫一個接受值的方法,Go語言會聰明地將該指標解除參照,並將指標所指的底層值作為方法的接收者。

型別 *T ⽅法集包含全部 receiver T + *T ⽅法:

type Person struct{
    name string
    sex byte
    age int
}
//指標作為接收者,參照語意
func (p *Person) SetInfoPointer(){
    (*p).name="yoyo"
    p.sex='f'
    p.age=22
}
//值作為接收者,值語意
func (p Person) SetInfoValue(){
    p.name="xxx"
    p.sex='m'
    p.age=33
}
func main() {
    //p為指標型別
    var p*Person = &Person{"mike",'m',18}
    p.SetInfoPointer()    //func (p)SetInfoPointer()
    p.SetInfoValue()    //func (*p)SetInfoValue()
    (*p).SetInfoValue()    //func (*p)SetInfoValue()
}

4.2型別 T 方法集

一個自定義型別值的方法集則由為該型別定義的接收者型別為值型別的方法組成,但是不包含那些接收者型別為指標的方法。

但這種限制通常並不像這裡所說的那樣,因為如果我們只有一個值,仍然可以呼叫一個接收者為指標型別的方法,這可以藉助於Go語言傳值的地址能力實現。

type Person struct{
    name string
    sex byte
    age int
}
//指標作為接收者,參照語意
func (p *Person) SetInfoPointer(){
    (*p).name="yoyo"
    p.sex='f'
    p.age=22
}
//值作為接收者,值語意
func (p Person)SetInfoValue(){
    p.name="xxx"
    p.sex='m'
    p.age=33
}
func main() {
    //p為普通值型別
    var p Person = Person{"mike",'m',18}
    (&p).SetInfoPointer()    //func(&p)SetInfoPointer()
    p.SetInfoPointer()    //func(&p)SetInfoPointer()
    p.SetInfoValue()    //func(p)SetInfoValue()
    (&p).SetInfoValue()    //func(*&p)SetInfoValue()
}

5匿名欄位

5.1方法的繼承

如果匿名欄位實現了一個方法,那麼包含這個匿名欄位的struct也能呼叫該方法。

type Person struct {
    name string
    sex byte
    age int
}
//Person定義了方法
func (p *Person) PrintInfo() {
    fmt.Printf("%s,%c,%dn",p.name,p.sex,p.age)
}
type Student struct {
    Person//匿名欄位,那麼Student包含了Person的所有欄位
    id int
    addr string
}
func main() {
    p := Person{"mike",'m',18}
    p.PrintInfo()
    s := Student{Person{"yoyo",'f',20},2,"sz"}
    s.PrintInfo()
}

5.2方法的重寫

type Person struct {
    name string
    sex byte
    age int
}
//Person定義了方法
func (p *Person) PrintInfo() {
    fmt.Printf("Person:%s,%c,%dn",p.name,p.sex,p.age)
}
type Student struct {
    Person//匿名欄位,那麼Student包含了Person的所有欄位
    id int
    addr string
}
//Student定義了方法
func (s *Student) PrintInfo() {
    fmt.Printf("Student:%s,%c,%dn",s.name,s.sex,s.age)
}
func main() {
    p:=Person{"mike",'m',18}
    p.PrintInfo()    //Person:mike,m,18
    s:=Student{Person{"yoyo",'f',20},2,"sz"}
    s.PrintInfo()    //Student:yoyo,f,20
    s.Person.PrintInfo()    //Person:yoyo,f,20
}

6方法值和方法表示式

類似於我們可以對函數進行賦值和傳遞一樣,方法也可以進行賦值和傳遞。

根據呼叫者不同,方法分為兩種表現形式:方法值和方法表示式。兩者都可像普通函數那樣賦值和傳參,區別在於方法值繫結範例,⽽方法表示式則須顯式傳參。

6.1方法值

type Person struct{
    name string
    sex byte
    age int
}
func (p *Person) PrintInfoPointer() {
    fmt.Printf("%p,%vn",p,p)
}
func (p Person) PrintInfoValue(){
    fmt.Printf("%p,%vn",&p,p)
}
func main() {
    p:=Person{"mike",'m',18}
    p.PrintInfoPointer()    //0xc0420023e0,&{mike 109 18}
    pFunc1:=p.PrintInfoPointer    //方法值,隱式傳遞 receiver
    pFunc1()    //0xc0420023e0,&{mike 109 18}
    pFunc2:=p.PrintInfoValue
    pFunc2()    //0xc042048420,{mike 109 18}
}

6.2方法表示式

type Person struct {
    name string
    sex byte
    age int
}
func (p *Person) PrintInfoPointer() {
    fmt.Printf("%p,%vn",p,p)
}
func (p Person) PrintInfoValue() {
    fmt.Printf("%p,%vn",&p,p)
}
func main() {
    p:=Person{"mike",'m',18}
    p.PrintInfoPointer()//0xc0420023e0,&{mike 109 18}
    //方法表示式,須顯式傳參
    //func pFunc1 (p *Person))
    pFunc1:=(*Person).PrintInfoPointer
    pFunc1(&p)    //0xc0420023e0,&{mike 109 18}
    pFunc2:=Person.PrintInfoValue
    pFunc2(p)    //0xc042002460,{mike 109 18}
}

以上就是go語言方法集以及為型別新增方法的範例解析的詳細內容,更多關於go語言方法集型別新增方法的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com