<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Go語言的物件導向的知識點時,發現它的物件導向能力全靠 interface 撐著,而且它的 interface 還與我們以前知道的 interface 完全不同。故而整個過程不斷的思考為什麼要如此設計?這樣設計給我們帶來了什麼影響?
interface(介面)是golang最重要的特性之一,實現多型。Interface型別可以定義一組方法,但是這些不需要實現。並且interface不能包含任何變數。
定義一個介面
type Person interface { // 宣告方法 method1(參數列)返回值列表 method2(參數列)返回值列表 }
實現一個介面
func (t 自定義型別)method1(參數列)返回值列表 { //方法實現 } func (t 自定義型別)method2(參數列)返回值列表 { //方法實現 }
小結:
(1)介面裡的所有方法都沒有方法體,即介面的方法都是沒有實現的方法。介面體現了程式設計的多型和高內聚低耦合的思想。
(2)Go中的介面,不需要顯示的實現。只要一個變數,含有介面型別中的所有方法,那麼這個變數就實現這個介面。因此,Go中沒有implement關鍵字樣。
(3)Go實現介面與方法有關,與介面本身叫什麼名字沒有特別大的關係。變數需要實現介面所有的方法。
(1)介面本身不能建立範例,但是可以指向一個實現了該介面的自定義型別的變數(範例)。
package main import "fmt" // Person 定義介面 type Person interface { GetName() string GetAge() uint32 } // Student 定義型別 type Student struct { Name string Age uint32 } func (s Student) GetName() string{ return s.Name } func (s Student) GetAge() uint32{ return s.Age } func main() { var student Student student.Age = 12 student.Name = "小明" var person Person person = student //介面執行向student fmt.Printf("name:%s,age: %dn", person.GetName(), person.GetAge()) }
(2)介面中所有的方法都沒有方法體,即都是沒有實現的方法。
(3)在Go中,一個自定義型別需要將某個介面的所有方法都實現,我們說這個自定義型別實現了該介面。
(4)一個自定義型別只有實現了某個介面,才能將該自定義型別的範例(變數)賦給介面型別。
(5)只要是自定義資料型別就可以實現介面,不僅僅是結構體型別。
(6)一個自定義型別可以實現多個介面。
(7)Go介面不能有任何變數。
(8)一個介面可以繼承多個別的介面,這時如果要實現這個介面必須實現它繼承的所有介面的方法。在低版本的Go編輯器中,一個介面繼承其他多個介面時,不允許繼承的介面有相同的方法名。比如A介面繼承B、C介面,B、C介面的方法名不能一樣。高版本的Go編輯器沒有相關問題。
(9)interface型別預設是一個指標(參照型別),如果沒有對interface初始化就使用,那麼會輸出nil。
(10)空介面interface{}沒有任何方法,所以所有型別都實現了空介面,即我們可以把任何一個變數賦給空介面型別。
Go的interface原始碼在Golang原始碼的runtime目錄中。
Go的interface是由兩種型別來實現的:iface和eface。
iface是包含方法的interface,如:
type Person interface { Print() }
iface的原始碼是:
type iface struct { tab *itab data unsafe.Pointer }
iface具體結構是:
itab是iface不同於eface的關鍵資料結構。其包含兩部分:一部分是唯一確定包含該interface的具體結構型別,一部分是指向具體方法集的指標。其具體結構為:
屬性 itab的原始碼是:
type itab struct { inter *interfacetype //此屬性用於定位到具體interface _type *_type //此屬性用於定位到具體interface hash uint32 // copy of _type.hash. Used for type switches. _ [4]byte fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. }
屬性interfacetype類似於_type,其作用就是interface的公共描述,類似的還有maptype、arraytype、chantype…其都是各個結構的公共描述,可以理解為一種外在的表現資訊。interfacetype原始碼如下:
type interfacetype struct { typ _type pkgpath name mhdr []imethod } type imethod struct { name nameOff ityp typeOff }
iface的整體結構為:
我們來看一個例子,對於含有方法的interface賦值後的內部結構是怎樣的呢?
package main import "fmt" // Person 定義介面 type Person interface { GetName() string GetAge() uint32 } // Student 定義型別 type Student struct { Name string Age uint32 } func (s Student) GetName() string{ return s.Name } func (s Student) GetAge() uint32{ return s.Age } func main() { var student Student student.Age = 12 student.Name = "小明" var person Person person = student fmt.Printf("name:%s,age: %dn", person.GetName(), person.GetAge()) }
執行結果:
name:小明,age: 12
Process finished with the exit code 0
記憶體分佈示意圖:
eface是不包含方法的interface,即空interface,如:
type Person interface { }
或者
var person interface{} = xxxx實體
侵入式
你的程式碼裡已經嵌入了別的程式碼,這些程式碼可能是你引入過的框架,也可能是你通過介面繼承得來的,比如:java中的繼承,必須顯示的表明我要繼承那個介面,這樣你就可以擁有侵入程式碼的一些功能。所以我們就稱這段程式碼是侵入式程式碼。
優點:通過侵入程式碼與你的程式碼結合可以更好的利用侵入程式碼提供給的功能。
缺點:框架外程式碼就不能使用了,不利於程式碼複用。依賴太多重構程式碼太痛苦了。
非侵入式
正好與侵入式相反,你的程式碼沒有引入別的包或框架,完完全全是自主開發。比如go中的介面,不需要顯示的繼承介面,只需要實現介面的所有方法就叫實現了該介面,即便該介面刪掉了,也不會影響我,所有go語言的介面數非侵入式介面;再如Python所崇尚的鴨子型別。
優點:程式碼可複用,方便移植。非侵入式也體現了程式碼的設計原則:高內聚,低耦合。
缺點:無法複用框架提供的程式碼和功能。
接下來看看java與go語言程式設計實現介面來理解侵入式與非侵入式的區別。
java語言實現
定義介面
public interface IPersonService { String getName(); Integer getAge(); }
實現介面的類
public class PersonService implements IPersonService{ @Override public String getName() { return "小明"; } @Override public Integer getAge() { return 12; } }
go語言實現
package main import "fmt" // Person 定義介面 type Person interface { GetName() string GetAge() uint32 } // Student 定義型別 type Student struct { Name string Age uint32 } func (s Student) GetName() string{ return s.Name } func (s Student) GetAge() uint32{ return s.Age } func main() { var student Student student.Age = 12 student.Name = "小明" var person Person person = student fmt.Printf("name:%s,age: %dn", person.GetName(), person.GetAge()) }
通過上面的例子我們總結了以下問題:
型別推斷可將介面變數還原為原始型別,或用來判斷是否實現了某個更具體的介面型別。
type data int func(d data)String()string{ return fmt.Sprintf("data:%d",d) } func main() { var d data=15 var x interface{} =d if n,ok:=x.(fmt.Stringer);ok{ // 轉換為更具體的介面型別 fmt.Println(n) } if d2,ok:=x.(data);ok{ // 轉換回原始型別 fmt.Println(d2) } e:=x.(error) // 錯誤:main.data is not error fmt.Println(e) }
輸出為:
data:15
data:15
panic:interface conversion:main.data is not error:missing method Error
但是此處會觸發panic,使用ok-idiom模式,即便轉換失敗也不會引發panic。還可用switch語句在多種型別間做出推斷匹配,這樣空介面就有更多發揮空間。
func main() { var x interface{} =func(x int)string{ return fmt.Sprintf("d:%d",x) } switch v:=x.(type) { // 區域性變數v是型別轉換後的結果 case nil: println("nil") case*int: println(*v) case func(int)string: println(v(100)) case fmt.Stringer: fmt.Println(v) default: println("unknown") } }
輸出為:
d:100
多型功能是interface實現的重要功能,也是Golang中的一大行為特色,其多型功能一般要結合Go method實現,作為函數引數可以容易的實現多臺功能。
package main import "fmt" // notifier是一個定義了通知類行為的介面 type notifier interface { notify() } // 定義user及user.notify方法 type user struct { name string email string } func (u *user) notify() { fmt.Printf("Sending user email to %s<%s>n", u.name, u.email) } // 定義admin及admin.notify方法 type admin struct { name string email string } func (a *admin) notify() { fmt.Printf("Sending admin email to %s<%s>n", a.name, a.email) } func main() { // 建立一個user值並傳給sendNotification bill := user{"Bill", "bill@email.com"} sendNotification(&bill) // 建立一個admin值並傳給sendNotification lisa := admin{"Lisa", "lisa@email.com"} sendNotification(&lisa) } // sendNotification接受一個實現了notifier介面的值 // 並行送通知 func sendNotification(n notifier) { n.notify() }
上述程式碼中實現了一個多型的例子,函數sendNotification接受一個實現了notifier介面的值作為引數。既然任意一個實體型別都能實現該介面,那麼這個函數可以針對任意實體型別的值來執行notify方法,呼叫notify時,會根據物件的實際定義來實現不同的行為,從而實現多型行為。
參照公司內部同事的討論議題,覺得之前自己也沒有理解明白,為此,單獨羅列出來,例子是最好的說明,如下
package main import ( "fmt" "reflect" ) type State struct{} func testnil1(a, b interface{}) bool { return a == b } func testnil2(a *State, b interface{}) bool { return a == b } func testnil3(a interface{}) bool { return a == nil } func testnil4(a *State) bool { return a == nil } func testnil5(a interface{}) bool { v := reflect.ValueOf(a) return !v.IsValid() || v.IsNil() } func main() { var a *State fmt.Println(testnil1(a, nil)) fmt.Println(testnil2(a, nil)) fmt.Println(testnil3(a)) fmt.Println(testnil4(a)) fmt.Println(testnil5(a)) }
返回結果如下
false
false
false
true
true
為啥呢?
一個interface{}型別的變數包含了2個指標,一個指標指向值的型別,另外一個指標指向實際的值 對一個interface{}型別的nil變數來說,它的兩個指標都是0;但是var a *State傳進去後,指向的型別的指標不為0了,因為有型別了, 所以比較為false。 interface 型別比較, 要是 兩個指標都相等, 才能相等。
到此這篇關於Go語言中interface語法與使用詳解的文章就介紹到這了,更多相關Go語言 interface詳解內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45