原文:CalliCoder — Golang Interfaces Tutorial with Examples
Go 的介面
Go 中的介面是使用一組方法簽名定義的型別。介面定義了類似物件型別的行為。
舉例來說,這是一個定義幾何形狀行為的介面:
// Go Interface - `Shape`
type Shape interface {
Area() float64
Perimeter() float64
}
使用 type 關鍵字定義介面,後面接著介面名稱和關鍵字 interface
。然後,在大括號內指定一組方法簽名。
在 Go 中實作介面
要實作介面,你只需要實作介面中宣告的所有方法。
Go 的介面是隱含實作的
與 Java 等其他語言不同,你不需要使用如 implements
關鍵字之類的方法來明確指定一種型別來實作介面。
以下是兩種實作 Shape
介面的 struct 型別:
// Struct type `Rectangle` - implements the `Shape` interface by implementing all its methods.
type Rectangle struct {
Length, Width float64
}
func (r Rectangle) Area() float64 {
return r.Length * r.Width
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Length + r.Width)
}
// Struct type `Circle` - implements the `Shape` interface by implementing all its methods.
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
func (c Circle) Diameter() float64 {
return 2 * c.Radius
}
使用具有明確數值的介面型別
除非我們將其與實作所有方法的具體型別一起使用,否則介面本身並不是那麼有用。
讓我們來看看如何將介面與明確數值一起使用。
介面型別可以包含實作其所有方法的任何值
package main import ( "fmt" "math" ) func main() { var s Shape = Circle{5.0} fmt.Printf("Shape Type = %T, Shape Value = %v\n", s, s) fmt.Printf("Area = %f, Perimeter = %f\n\n", s.Area(), s.Perimeter()) var s1 Shape = Rectangle{4.0, 6.0} fmt.Printf("Shape Type = %T, Shape Value = %v\n", s1, s1) fmt.Printf("Area = %f, Perimeter = %f\n", s1.Area(), s1.Perimeter()) }
# Output Shape Type = main.Circle, Shape Value = {5} Area = 78.539816, Perimeter = 31.415927 Shape Type = main.Rectangle, Shape Value = {4 6} Area = 24.000000, Perimeter = 20.000000
使用介面型別作為函數的參數
package main import "fmt" // Generic function to calculate the total area of multiple shapes of different types func CalculateTotalArea(shapes ...Shape) float64 { totalArea := 0.0 for _, s := range shapes { totalArea += s.Area() } return totalArea } func main() { totalArea := CalculateTotalArea(Circle{2}, Rectangle{4, 5}, Circle{10}) fmt.Println("Total area = ", totalArea) }
# Output Total area = 346.7256359733385
將介面型別用於欄位
package main import "fmt" // Interface types can also be used as fields type MyDrawing struct { shapes []Shape bgColor string fgColor string } func (drawing MyDrawing) Area() float64 { totalArea := 0.0 for _, s := range drawing.shapes { totalArea += s.Area() } return totalArea } func main() { drawing := MyDrawing{ shapes: []Shape{ Circle{2}, Rectangle{3, 5}, Rectangle{4, 7}, }, bgColor: "red", fgColor: "white", } fmt.Println("Drawing", drawing) fmt.Println("Drawing Area = ", drawing.Area()) }
# Output Drawing {[{2} {3 5} {4 7}] red white} Drawing Area = 55.56637061435917
介面數值:介面型別如何與明確數值一起使用?
在底層,介面值可以認為是一個由值和具體型別組成的元組:
// interface
(value, type)
讓我們來看一個範例了解更多資訊:
package main
import "fmt"
func main() {
var s Shape
s = Circle{5}
fmt.Printf("(%v, %T)\n", s, s)
fmt.Printf("Shape area = %v\n", s.Area())
s = Rectangle{4, 7}
fmt.Printf("(%v, %T)\n", s, s)
fmt.Printf("Shape area = %v\n", s.Area())
}
# Output
({5}, main.Circle)
Shape area = 78.53981633974483
({4 7}, main.Rectangle)
Shape area = 28
查看以上程式的輸出,並注意變數 s
如何取得有關數值以及分配給它的 Shape
的型別的資訊。
當我們在介面上呼叫方法時,將執行其底層型別上同名的方法。
來說,在上面的程式中,當我們在變數 s
上呼叫 Area()
方法時,它將執行其底層型別 Area()
方法。