十二、方法

方法是爲特定類型定義的,只能由該類型調用的函數

1.定義&調用

package main

import (
   "fmt"
)

type Dog struct {
   Name string
}

//方法是爲特定類型定義的,只能由該類型調用的函數
//方法是添加了接收者的函數,接收者必須是自定義的類型
func (dog Dog) Call() {
   fmt.Printf("%s:汪汪\n", dog.Name)
}

//自動解引用 取引用只會在接收者  函數的參數 以及方法的參數是不可以的 需要手動取or解引用
//值接收者
func (dog Dog) SetName(name string) {
   dog.Name = name
}

//指針接收者  nil指針不能調用方法
func (dog *Dog) PsetName(name string) {
   dog.Name = name
}
func main() {
   //調用方法通過自定義類型的對象.方法名進行調用,在調用過程中對象傳遞(賦值)給方法的接收者(值類型,拷貝)
   dog := Dog{"豆豆"}
   dog.Call()
   dog.SetName("小黑")
   dog.Call()
   //調用者 是一個值類型  方法接收是一個指針接收者 會自動取引用
   (&dog).PsetName("aaa") //取引用
   dog.Call()
   dog.PsetName("小白") //自動取引用 語法糖
   dog.Call()
   //調用者 是一個指針類型類型  方法接收是一個值類型接收者 會自動解引用
   pdog := &Dog{"無敵"}
   (*pdog).Call()
   pdog.PsetName("haha")
   // (*pdog).Call()  解引用
   pdog.Call() //自動解引用      語法糖
}

2.匿名嵌入

若結構體匿名嵌入帶有方法的結構體時,則在外部結構體可以調用嵌入結構體的方法,並且
在調用時只有嵌入的字段會傳遞給嵌入結構體方法的接收者。
當被嵌入結構體與嵌入結構體具有相同名稱的方法時,則使用對象.方法名調用被嵌入結構
體方法。若想要調用嵌入結構體方法,則使用對象.嵌入結構體名.方法
(1)結構體中嵌入帶方法的命名結構體

package main

import (
   "fmt"
)

type Addrs struct {
   Region string
   Street string
}

type Users struct {
   ID   int
   Name string
   Addr Addrs
}

// func (addr Addrs) Add() string {
func (addr Addrs) String() string {
   return fmt.Sprintf("%v-%v", addr.Region, addr.Street)
}

//這裏的區別就是String函數 會返回一個比較可觀的,當沒有String方法 會拼接成大括號
// func (user Users) User() string {
func (user Users) String() string {
   return fmt.Sprintf("[%v]%v:%v", user.ID, user.Name, user.Addr)
}

func main() {
   m := Users{
      ID:   1,
      Name: "aa",
      Addr: Addrs{
         Region: "北京市",
         Street: "宋家莊",
      },
   }
   // fmt.Println(m.User())
   // fmt.Println(m.Addr.Add())
   fmt.Println(m)
   fmt.Println(m.Addr)

}

(2)結構體中嵌入帶方法的匿名結構體v1

package main

import "fmt"
//匿名方法嵌入
type User struct {
   ID int
   Name string
}

type Addr struct {
   RB string
   User
}

func (user User) GetID() int{
   return user.ID
}

func (user User) GetName() string  {
   return user.Name
}

func (user *User) SetID(id int) {
   user.ID = id
}

func (user *User) SetName(name string) {
   user.Name = name
}

func main() {
   me := Addr{
      RB: "aa",
      User: User{
         ID: 5,
         Name: "bb",
      },
   }
   fmt.Println(me.GetID())
   fmt.Println(me.GetName())
   me.SetName("無敵")
   me.SetID(100)
   fmt.Println(me.GetID())
   //當匿名結構體嵌入帶有方法的結構體訪問,變量.匿名結構體name.方法 或者可以省略匿名結構體name
   //當匿名結構體的方法跟外部結構體的方法名相同時,需要使用絕對路徑調用方法例如 結構體.方法  結構體.匿名結構體.方法
   fmt.Println(me.GetName())
   fmt.Println(me.User.GetName())
}

(3)結構體中嵌入帶方法的匿名結構體v2

package main

import "fmt"
//匿名方法嵌入
type User struct {
   ID int
   Name string
}

type Addr struct {
   RB string
   User
   Name string
}

func (user User) GetID() int{
   return user.ID
}

func (user User) GetName() string  {
   return user.Name
}

func (user *User) SetID(id int) {
   user.ID = id
}

func (user *User) SetName(name string) {
   user.Name = name
}

func (addr  Addr) GetAddrName() string  {
   return addr.Name
}
//這裏要區分匿名嵌入修改屬性修改的是那個屬性  外層結構體屬性與嵌套結構體屬性name相同時
func (addr  *Addr) SetAddrName(name string)   {
   addr.Name = name
}


func main() {
   me := Addr{
      RB: "aa",
      User: User{
         ID: 5,
         Name: "bb",
      },
      Name: "ccc",
   }
   fmt.Println(me.GetID())
   fmt.Println(me.GetName())
   me.SetName("無敵")
   me.SetID(100)
   fmt.Println(me.GetID())
   fmt.Println(me.GetName())

   fmt.Println(me.GetAddrName())
   me.SetAddrName("bbb")
   fmt.Printf("%#v",me)
}

3.方法值/方法表達式

方法也可以賦值給變量,存儲在數組、切片、映射中,也可作爲參數傳遞給函數或作爲函數
返回值進行返回
方法有兩種,一種時使用對象/對象指針調用的(方法值),另一種時有類型/類型指針調用的
(方法表達式)
(1)方法值
在方法值賦值時若方法接收者爲值類型,則在賦值時會將值類型拷貝(若調用爲指針則
自動解引用拷貝,這裏拷貝的是一個內存地址)

package main

import "fmt"

type Dog struct {
   Name string
   Age int
}

func (dog Dog) Call() {
   fmt.Printf("%s:汪汪\n",dog.Name)
}

func (dog *Dog) SetName(name string) {
   dog.Name = name
}

func main() {
   m := Dog{
      Name: "小黑",
      Age:  1,
   }
   //值接收者方法 賦值都把值對象進行一個copy
   m1 := m.Call  //這裏是copy 修改並不會影響原先的值
   fmt.Printf("%T\n",m1)
   m1()
   m.SetName("bbb")
   m1()
   m.Call()

   me := &Dog{
      Name: "小黑",
      Age:  1,
   }
   m2 := me.Call   //me是指針類型 這裏在賦值的時候 是先解引用 變成值類型 在copy 所以修改之後的不會影響之前的
   fmt.Printf("%T\n",m2)
   m2()
   me.SetName("ccc")
   m2()
   me.Call()

}
package main

import "fmt"
//方法值 就是獲取一個對象把他的方法賦值給一個函數
type Dog struct {
   Name string
   Age int
}

func (dog *Dog) Call() {
   fmt.Printf("%s:汪汪\n",dog.Name)
}

func (dog *Dog) SetName(name string) {
   dog.Name = name
}

func main() {
   m := Dog{
      Name: "小黑",
      Age:  1,
   }
   //指針接收者  賦值 解引用後是一個內存地址 賦值之後共享一個內存地址 所以會變
   m1 := m.Call  //這裏會自動解用
   fmt.Printf("%T\n",m1)
   m1()
   m.SetName("bbb")
   m1()
   m.Call()

   me := &Dog{
      Name: "小黑",
      Age:  1,
   }

   m2 := me.Call
   fmt.Printf("%T\n",m2)
   m2()
   me.SetName("ccc")
   m2()
   me.Call()

}

(2)方法表達式

方法表達式在賦值時,針對接收者爲值類型的方法使用類型名或類型指針訪問(go 自動爲
指針變量生成隱式的指針類型接收者方法),針對接收者爲指針類型則使用類型指針訪問。
同時在調用時需要傳遞對應的值對象或指針對象

package main

import (
   "fmt"
)
//方法表達式
type Dog struct {
   Name string
}
//定義值接收者Dog
//用值來訪問Dog.Call
func (dog Dog) Call() {
   fmt.Printf("%s:汪汪\n",dog.Name)
}
//定義指針接收者Dog
//指針接收者調用SetName  *Dog.SetName
func (dog *Dog) SetName(name string) {
   dog.Name = name
}

func main() {
   m1 := Dog.Call
   m2 := (*Dog).SetName
   dog := Dog{"aaa"}
   fmt.Printf("%T,%T\n",m1,m2)
   m1(dog)
   m2(&dog,"bbb")
   m1(dog)
   dog.SetName("ccc")
   m1(dog)
   //方法表達式需要自己引用以及解引用
   pdog := &Dog{"嘿嘿"}
   m1(*pdog)
   m2(pdog,"bbb")
   m1(*pdog)


}

(3)自動生成指針接收者方法

package main

import (
   "fmt"
)
//方法表達式
type Dog struct {
   Name string
}

func (dog Dog) Call() {
   fmt.Printf("%s:汪汪\n",dog.Name)
}
/*
會根據值接收者方法自動生成指針接收者方法
但是不會根據指針接收者方法生成值接收者方法
func (dog *Dog) Call() {
   獲取dog地址的值並拷貝調用call 隻影響拷貝(*user)的值,並不影響調用者的值
   與(dog dog)Call() 行爲一致 不會修改原內容
   fmt.Printf("%s:汪汪\n",(*dog).Name)
}
 */
func (dog *Dog) SetName(name string) {
   dog.Name = name
}
//指針類型的接收者是爲了修改  如果生成值類型的接收者則無法修改 所以值類型的可以自動生成指針類型,但是指針類型不能生成值類型
func main() {
   dog := Dog{"bbb"}
   m := (*Dog).Call
   m(&dog)

   pdog := &Dog{"ccc"}
   m(pdog)

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