Go開發工程師
未來3-5年企業高性能項目不可替代的語言,從基礎到項目實戰再到重構,真正從入門到精通
在Go語言中,一個接口類型總是代表著某一種類型(即所有實現它的類型)的行為。一個接口類型的聲明通常會包含關鍵字type
、類型名稱、關鍵字interface
以及由花括號包裹的若干方法聲明。示例如下:
type Animal interface { Grow() Move(string) string }
注意,接口類型中的方法聲明是普通的方法聲明的簡化形式。它們只包括方法名稱、參數聲明列表和結果聲明列表。其中的參數的名稱和結果的名稱都可以被省略。不過,出于文檔化的目的,我還是建議大家在這里寫上它們。因此,Move
方法的聲明至少應該是這樣的:
Move(new string) (old string)
如果一個數據類型所擁有的方法集合中包含了某一個接口類型中的所有方法聲明的實現,那么就可以說這個數據類型實現了那個接口類型。所謂實現一個接口中的方法是指,具有與該方法相同的聲明并且添加了實現部分(由花括號包裹的若干條語句)。相同的方法聲明意味著完全一致的名稱、參數類型列表和結果類型列表。其中,參數類型列表即為參數聲明列表中除去參數名稱的部分。一致的參數類型列表意味著其長度以及順序的完全相同。對于結果類型列表也是如此。
例如,如果你正確地完成了上一小節的練習的話,*Person
類型(注意,不是Person
類型)就會擁有一個Move
方法。該方法會是Animal
接口的Move
方法的一個實現。再加上我們在之前為它編寫的那個Grow
方法,*Person
類型就可以被看做是Animal
接口的一個實現類型了。
你可能已經意識到,我們無需在一個數據類型中聲明它實現了哪個接口。只要滿足了“方法集合為其超集”的條件,就建立了“實現”關系。這是典型的無侵入式的接口實現方法。
好了,現在我們已經認為*Person
類型實現了Animal
接口。但是Go語言編譯器是否也這樣認為呢?這顯然需要一種顯式的判定方法。在Go語言中,這種判定可以用類型斷言來實現。不過,在這里,我們是不能在一個非接口類型的值上應用類型斷言來判定它是否屬于某一個接口類型的。我們必須先把前者轉換成空接口類型的值。這又涉及到了Go語言的類型轉換。
Go語言的類型轉換規則定義了是否能夠以及怎樣可以把一個類型的值轉換另一個類型的值。另一方面,所謂空接口類型即是不包含任何方法聲明的接口類型,用interface{}
表示,常簡稱為空接口。正因為空接口的定義,Go語言中的包含預定義的任何數據類型都可以被看做是空接口的實現。我們可以直接使用類型轉換表達式把一個*Person
類型轉換成空接口類型的值,就像這樣:
p := Person{"Robert", "Male", 33, "Beijing"} v := interface{}(&p)
請注意第二行。在類型字面量后跟由圓括號包裹的值(或能夠代表它的變量、常量或表達式)就構成了一個類型轉換表達式,意為將后者轉換為前者類型的值。在這里,我們把表達式&p
的求值結果轉換成了一個空接口類型的值,并由變量v
代表。注意,表達式&p
(&
是取址操作符)的求值結果是一個*Person
類型的值,即p
的指針。
在這之后,我們就可以在v
上應用類型斷言了,即:
h, ok := v.(Animal)
類型斷言表達式v.(Animal)
的求值結果可以有兩個。第一個結果是被轉換后的那個目標類型(這里是Animal
)的值,而第二個結果則是轉換操作成功與否的標志。顯然,ok
代表了一個bool
類型的值。它也是這里判定實現關系的重要依據。
至此,我們掌握了接口類型、實現類型以及實現關系判定的重要知識和技巧。關于Go語言的類型轉換規則的更多細節請參看Go語言規范或《Go并發編程實戰》中的相關內容。而至于為什么只有*Person
類型才實現了Animal
接口,請參看后面兩節。
在源碼文件的第10行處加入若干代碼,使該文件不出現任何編譯錯誤,并且運行該文件會使標準輸出上出現true, &{Little C 2 In the house}
。
在第10行加入的代碼可以是:
type Cat struct { Name string Age uint8 Location string } func (cat *Cat) Grow() { cat.Age++ } func (cat *Cat) Move(new string) string { old := cat.Location cat.Location = new return old }
注意,答案不是唯一的。
請驗證,完成請求
由于請求次數過多,請先驗證,完成再次請求
打開微信掃碼自動綁定
綁定后可得到
使用 Ctrl+D 可將課程添加到書簽
舉報