Calvert's murmur

Golang 介面教學與範例

2019-11-19

約 3128 字 / 需 17 分鐘閱讀

原文:CalliCoderGolang 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() 方法。

Tags: Golang