go reflect 取指標 Go的方法集詳解

2021-10-13 17:28:10 字數 4705 閱讀 8886

女主宣言

go語言以其本身具有的高併發特性,在雲計算開發中,得到了廣泛的應用,也深受廣大開發者的歡迎。但是大家對go語言真的了解了麼?本文作者經過對go語言的多年實踐應用,現對go語言中的方法集進行了一次詳細的總結,並通過實驗進行了驗證,相信對於go語言愛好者有很大的幫助。下來就跟隨作者一起學習下吧。

ps:豐富的一線技術、多元化的表現形式,盡在「360雲計算」,點關注哦!

在go語言中,每個型別都有與之關聯的方法,把這個型別的所有方法稱為型別的方法集。如下:

type student struct   func (s student) showname()  func (s * student) setname(newname string)
型別student方法集包含了showname()方法。型別*student方法集包含了showname()方法和setname()方法。為什麼呢?因為:

方法集和方法接受者的關係

在上面的案例中,型別student的方法集並不包含了setname()方法,那麼是不是student型別變數,就不能呼叫setname()方法呢?即下面呼叫,是否會報錯呢?

s := student{}s.setname("dq")
其實,上面的呼叫是ok的。為什麼呢?我們來回顧一下go語言的方法定義。

如下:

type student struct type studentpoint *student func (student) sayhello()  func (s student) showname()  func (s * student) setname(newname string)  func (s studentpoint) showname2() s := student{}s.setname("dq") //go會自動轉為 (&s).setname("dq") var s2 = &ss2.showname() //o會自動轉為 (*s2).showname()
所以,當型別呼叫自己申明的方法的時候,不需要考慮方法集。方法接受者是值型別(t),還是指標型別(*t),影響t型別的實體變數的方法集。

方法集和介面

介面的定義

介面是乙個或多個方法簽名的集合。任何型別的方法集中只要擁有該介面「對應的全部方法」聲名。就表示它 "實現" 了該介面,無須在該型別上顯式宣告實現了哪個介面。對應的全部方法:是指有相同名稱、引數列表 (不包括引數名) 以及返回值。介面只有方法的宣告,沒有實現。介面可以匿名嵌入到其他介面,或是嵌入結構體中。介面命名習慣以 er 結尾。

type personer inte***ce  type student struct
介面執行機制

介面物件,是由介面表(inte***ce table)和資料指標組成。介面表儲存元資料資訊,包括介面型別、動態型別,以及實現介面的方法指標。資料指標持有的是目標物件的唯讀複製品,複製完整物件或指標。

package main import (   "fmt"   "reflect") type user struct  type student struct  func main()    var i inte***ce{} = u // 由於inte***ce{}不包含任何方法,所以任何型別,都實現了inte***ce{}介面   fmt.println(reflect.typeof(i))     i = student{}   fmt.println(reflect.typeof(i))
以實體型別和以指標型別實現介面的區別

若以實體型別(t)實現介面,不管是t型別的值,還是t型別的指標,都實現了該介面。

若以指標型別(*t)實現介面,只有t型別的指標,才實現了該介面。

type animal inte***ce  type dog struct  type cat struct  func (_self dog) say()  func (_self *cat) say()  func main()    d1.say() //因為dog 的value型別實現了animal介面    var d2 animal= &dog   d2.say() //因為ddog 的指標型別實現了animal介面    var c1 animal= cat   c1.say() //因為cat 的value型別沒有實現了animal介面,所以報錯     var c2 animal= &cat   c2.say() //因為cat 的指標型別實現了animal介面}
型別必須實現介面的所有方法,才能表示它 "實現" 了該介面,如下:

type animal inte***ce  type dog struct func (_self dog) say() func (_self *dog) dosome()  func main()     d1.say()    //因為ddog 的指標型別實現了animal介面集的所有方法   var d2 animal= &dog   d2.say() }
方法集和嵌入

什麼是嵌入

go語言中,所謂的嵌入,即把乙個型別作為另外乙個型別的匿名字段。如下:

type person struct  type student struct
go語言通過嵌入組合,來實現繼承的行為。於是,我們就可以通過student型別的例項,訪問persion型別的變數和方法。如下:

var s = student{}s.name = "dq"
值型別(t)嵌入和指標型別(*t)嵌入的區別

type student1 struct type student2 struct
要理解這個區別,就有知道go語言中型別的預設值。如下:

所以:

type person struct  func (s person) showname()  func (s *person) setname(newname string)  type student1 struct  type student2 struct  // 內嵌型別 persion預設值為 persons1 := student1{}s1.setname("student1_01") // oks1.showname() // 內嵌型別 *persion預設值為 nils2 := &student2{}s2.setname("student1_02") //error,由於目前內嵌型別的值為nil,會觸發報錯s2.showname() // 給嵌入型別乙個複製,就ok了s3 := &student2}//s3 := &student2} 和上一行等價s3.showname()
在上面的案例中變數s2中嵌入型別預設值為nil,故會報錯:panic: runtime error: invalid memory address or nil pointer dereference所以,針對指標嵌入型別,在使用前,需要賦值。

嵌入和方法集的關係

下面,通過案例來解析,如下,思考student1的指標和例項型別,以及student2的指標和例項型別,是否實現了human介面呢?

type human inte***ce  type person struct func (s person) showname() func (s *person) setname(newname string)  type student1 struct  type student2 struct
解析:

應用上面規則1,由於student1通過實體型別(t)方式,嵌入person,所以 stuednt1的例項型別,僅僅包含了接受者為person的方法,即不包含setname()方法,所以student1的例項型別,沒有實現human介面,不能賦值給human介面;應用上面規則3, stuednt1的指標型別,包含了接受者為person和接受者為*person的方法,即stuednt1的指標型別,實現了human介面。

應用上面規則2,由於student2通過指標型別(*t)方式,嵌入person,所以student2的指標型別和例項型別,都實現了human介面。

所以:

// error 應用上面的關係判斷第1條規則,因為student1例項型別的方法集中,僅僅包含person的例項方法集,即僅僅包含showname()方法,所以student1的例項型別,沒有實現human介面var s1 human = student1{} //報錯:student1 does not implement human (setname method has pointer receiver)s1.setname("student1_01")s1.showname() var s2 human = &student1{} //ok 應用第1條和弟3條規則s2.setname("student1_02")s2.showname() var s3 human = student2} //ok ,應用第2條規則s3.setname("student2_01")s3.showname() var s4 human = &student2} //ok ,應用第2條規則s4.setname("student2_02")s4.showname()
360雲計算

go 指標陣列 go語言中的指標陣列

宣告乙個包含有5個整數指標型別的陣列,我們可以在初始化時給相應位置的元素預設值。下面是給索引為0的元素乙個新建的的int型別指標 預設為0 給索引為1的元素指向值v的位址,剩下的沒有指定預設值的元素為指標的zero值也就是nil package main import fmt func main f...

go 的指標理解

都說go指著跟 c 的比較像,其實不是很明白普通變數和指標的區別 看到有人說 其實 普通變數 是程式創造出來的,比如說 c 中 a int那麼在編譯時 就會有 a 位址 int 這樣來標識記憶體 普通變數a其實是語言本身創造了,是為了更方便的表示記憶體。我們對a進行訪問其實就是直接對記憶體進行訪問。...

Go 語言指標

b 什麼是指標 b 乙個指標變數指向了乙個值的記憶體位址。類似於變數和常量,在使用指標前你需要宣告指標。指標宣告格式如下 var var name var type var type 為指標型別,var name 為指標變數名,號用於指定變數是作為乙個指標。以下是有效的指標宣告 var ip int...