方法是爲特定類型定義的,只能由該類型調用的函數
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)
}