這是『就要學習 Go 語言』系列的第 20 篇分享文章golang
提醒:文末給你們留了小練習,能夠先看文章,再作練習,檢驗本身的學習成果!ide
咱們接着上一篇,繼續講接口的其餘用法。函數
一種類型能夠實現多個接口,來看下例子:學習
type Shape interface {
Area() float32
}
type Object interface {
Perimeter() float32
}
type Circle struct {
radius float32
}
func (c Circle) Area() float32 {
return math.Pi * (c.radius * c.radius)
}
func (c Circle) Perimeter() float32 {
return 2 * math.Pi * c.radius
}
func main() {
c := Circle{3}
var s Shape = c
var p Object = c
fmt.Println("area: ", s.Area())
fmt.Println("perimeter: ", p.Perimeter())
}
複製代碼
輸出:ui
area: 28.274334
perimeter: 18.849556
複製代碼
上面的代碼,結構體 Circle 分別實現了 Shape 接口和 Object 接口,因此能夠將結構體變量 c 賦給變量 s 和 p,此時 s 和 p 具備相同的動態類型和動態值,分別調用各自實現的方法 Area() 和 Perimeter()。 咱們修改下程序:spa
fmt.Println("area: ", p.Area())
fmt.Println("perimeter: ", s.Perimeter())
複製代碼
編譯會出錯:.net
p.Area undefined (type Object has no field or method Area)
s.Perimeter undefined (type Shape has no field or method Perimeter)
複製代碼
爲何?由於 s 的靜態類型是 Shape,而 p 的靜態類型是 Object。那有什麼解決辦法嗎?有的,咱們接着看下一節指針
類型斷言能夠用來獲取接口的底層值,一般的語法:i.(Type),其中 i 是接口,Type 是類型或接口。編譯時會自動檢測 i 的動態類型與 Type 是否一致。code
type Shape interface {
Area() float32
}
type Object interface {
Perimeter() float32
}
type Circle struct {
radius float32
}
func (c Circle) Area() float32 {
return math.Pi * (c.radius * c.radius)
}
func (c Circle) Perimeter() float32 {
return 2 * math.Pi * c.radius
}
func main() {
var s Shape = Circle{3}
c := s.(Circle)
fmt.Printf("%T\n",c)
fmt.Printf("%v\n",c)
fmt.Println("area: ", c.Area())
fmt.Println("perimeter: ", c.Perimeter())
}
複製代碼
輸出:cdn
main.Circle
{3}
area: 28.274334
perimeter: 18.849556
複製代碼
上面的代碼,咱們能夠經過 c 訪問接口 s 的底層值,也能夠經過 c 分別調用方法 Area() 和 Perimeter(),這就解決了上面遇到的問題。 在語法 i.(Type) 中,若是 Type 沒有實現 i 所屬的接口,編譯的時候會報錯;或者 i 的動態值不是 Type,則會報 panic 錯誤。怎麼解決呢?能夠使用下面的語法:
value, ok := i.(Type)
複製代碼
使用上面的語法,Go 會自動檢測上面提到的兩種狀況,咱們只須要經過變量 ok 判斷結果是否正確便可。若是正確,ok 爲 true,不然爲 false,value 爲 Type 對應的零值。
類型選擇用於將接口的具體類型與各類 case 語句中指定的多種類型進行匹配比較,有點相似於 switch case 語句,不一樣的是 case 中指定是類型。 類型選擇的語法有點相似於類型斷言的語法:i.(type),其中 i 是接口,type 是固定關鍵字,使用這個能夠得到接口的具體類型而不是值,每個 case 中的類型必須實現了 i 接口。
func switchType(i interface{}) {
switch i.(type) {
case string:
fmt.Printf("string and value is %s\n", i.(string))
case int:
fmt.Printf("int and value is %d\n", i.(int))
default:
fmt.Printf("Unknown type\n")
}
}
func main() {
switchType("Seekload")
switchType(27)
switchType(true)
}
複製代碼
輸出:
string and value is Seekload
int and value is 27
Unknown type
複製代碼
上面的代碼應該很好理解,i 的類型匹配到哪一個 case ,就會執行相應的輸出語句。 注意:只有接口類型才能夠進行類型選擇。其餘類型,例如 int、string等是不能的:
i := 1
switch i.(type) {
case int:
println("int type")
default:
println("unknown type")
}
複製代碼
報錯:
cannot type switch on non-interface value i (type int)
複製代碼
Go 語言中,接口不能去實現別的接口也不能繼承,可是能夠經過嵌套接口建立新接口。
type Math interface {
Shape
Object
}
type Shape interface {
Area() float32
}
type Object interface {
Perimeter() float32
}
type Circle struct {
radius float32
}
func (c Circle) Area() float32 {
return math.Pi * (c.radius * c.radius)
}
func (c Circle) Perimeter() float32 {
return 2 * math.Pi * c.radius
}
func main() {
c := Circle{3}
var m Math = c
fmt.Printf("%T\n", m )
fmt.Println("area: ", m.Area())
fmt.Println("perimeter: ", m.Perimeter())
}
複製代碼
輸出:
main.Circle
area: 28.274334
perimeter: 18.849556
複製代碼
上面的代碼,經過嵌套接口 Shape 和 Object,建立了新的接口 Math。任何類型若是實現了接口 Shape 和 Object 定義的方法,則說類型也實現了接口 Math,例如咱們建立的結構體 Circle。 主函數裏面,定義了接口類型的變量 m,動態類型是結構體 Circle,注意下方法 Area 和 Perimeter 的調用方式,相似與訪問嵌套結構體的成員。
在前面咱們都是經過值接收者去實現接口的,其實還能夠經過指針接收者實現接口。實現過程當中仍是有須要注意的地方,咱們來看下:
type Shape interface {
Area() float32
}
type Circle struct {
radius float32
}
type Square struct {
side float32
}
func (c Circle) Area() float32 {
return math.Pi * (c.radius * c.radius)
}
func (s *Square) Area() float32 {
return s.side * s.side
}
func main() {
var s Shape
c1 := Circle{3}
s = c1
fmt.Printf("%v\n",s.Area())
c2 := Circle{4}
s = &c2
fmt.Printf("%v\n",s.Area())
c3 := Square{3}
//s = c3
s = &c3
fmt.Printf("%v\n",s.Area())
}
複製代碼
輸出:
28.274334
50.265484
9
複製代碼
上面的代碼,結構體 Circle 經過值接收者實現了接口 Shape。咱們在方法那篇文章中已經討論過了,值接收者的方法能夠使用值或者指針調用,因此上面的 c1 和 c2 的調用方式是合法的。
結構體 Square 經過指針接收者實現了接口 Shape。若是將上方註釋部分打開的話,編譯就會出錯:
cannot use c3 (type Square) as type Shape in assignment:
Square does not implement Shape (Area method has pointer receiver)
複製代碼
從報錯提示信息能夠清楚看出,此時咱們嘗試將值類型 c3 分配給 s,但 c3 並無實現接口 Shape。這可能會令咱們有點驚訝,由於在方法中,咱們能夠直接經過值類型或者指針類型調用指針接收者方法。 記住一點:對於指針接受者的方法,用一個指針或者一個可取得地址的值來調用都是合法的。但接口存儲的具體值是不可尋址的,對於編譯器沒法自動獲取 c3 的地址,因而程序報錯。
關於接口的使用方法總結到這,但願這兩篇文章可以給你帶來幫助!
做業: 文章提到的類型斷言:i.(Type),其中 i 是接口,Type 能夠是類型或接口,若是 Type 是接口的話,表達式是什麼意思呢?下面的程序輸出什麼?
type Shape interface {
Area() float32
}
type Object interface {
Perimeter() float32
}
type Circle struct {
radius float32
}
func (c Circle) Area() float32 {
return math.Pi * (c.radius * c.radius)
}
func main() {
var s Shape = Circle{3}
value1,ok1 := s.(Shape)
value2,ok2 := s.(Object)
fmt.Println(value1,ok1)
fmt.Println(value2,ok2)
}
複製代碼
歡迎你們留言討論!
原創文章,若需轉載請註明出處!
歡迎掃碼關注公衆號「Golang來啦」或者移步 seekload.net ,查看更多精彩文章。
公衆號「Golang來啦」給你準備了一份神祕學習大禮包,後臺回覆【電子書】領取!