Skip to content

Go 接口

在 Go 中,接口(interface)是一种类型,它定义了一组方法签名(method signatures)。接口指定了一个类型必须实现哪些方法才能“满足”(satisfy)或“实现”(implement)该接口。与其他一些语言不同,Go 的接口是隐式满足的(implicitly satisfied)。如果一个类型实现了接口中声明的所有方法,它就自动满足该接口,无需显式使用 implements 关键字。

这种隐式满足的特性使得 Go 的接口非常灵活和强大,促进了松耦合(loose coupling)并实现了多态性(polymorphism)。

/* define an interface */
type InterfaceName interface {
MethodName1(param1 type1, param2 type2) returnType1
MethodName2() (returnTypeA, returnTypeB)
// ... other method signatures
}
/* define a struct (or any other named type) */
type ConcreteType struct {
// fields
}
/* implement interface methods for ConcreteType */
// Note: The receiver can be a value (ConcreteType) or a pointer (*ConcreteType)
func (c ConcreteType) MethodName1(param1 type1, param2 type2) returnType1 {
// method implementation
return // ... appropriate value
}
func (c ConcreteType) MethodName2() (returnTypeA, returnTypeB) {
// method implementation
return // ... appropriate values
}

让我们定义一个名为 Shape 的接口,它包含一个 Area() 方法。然后我们将创建实现此接口的 Circle 和 Rectangle 类型。

package main
import (
"fmt"
"math"
)
// Shape interface defines a contract for types that can calculate their area.
type Shape interface {
Area() float64
}
// Circle type with a radius.
type Circle struct {
Radius float64
}
// Rectangle type with width and height.
type Rectangle struct {
Width, Height float64
}
// Circle implements the Shape interface.
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// Rectangle implements the Shape interface.
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// printArea takes any Shape and prints its area.
// This demonstrates polymorphism.
func printArea(s Shape) {
fmt.Printf("Area of shape: %f\n", s.Area())
}
// calculateTotalArea calculates the sum of areas for a slice of Shapes.
func calculateTotalArea(shapes []Shape) float64 {
totalArea := 0.0
for _, s := range shapes {
totalArea += s.Area()
}
return totalArea
}
func main() {
circle := Circle{Radius: 5}
rectangle := Rectangle{Width: 10, Height: 5}
fmt.Printf("Circle area: %f\n", circle.Area()) // Direct call
fmt.Printf("Rectangle area: %f\n", rectangle.Area()) // Direct call
fmt.Println("\nUsing printArea function (polymorphism):")
printArea(circle) // Circle satisfies Shape
printArea(rectangle) // Rectangle satisfies Shape
shapes := []Shape{ // A slice of Shapes
Circle{Radius: 2.5},
Rectangle{Width: 3, Height: 7},
Circle{Radius: 10},
}
total := calculateTotalArea(shapes)
fmt.Printf("\nTotal area of all shapes: %f\n", total)
}

编译并执行上述代码后,会产生以下结果:

Circle area: 78.539816
Rectangle area: 50.000000
Using printArea function (polymorphism):
Area of shape: 78.539816
Area of shape: 50.000000
Total area of all shapes: 334.207943

没有定义任何方法的接口称为空接口(empty interface),写作 interface{}。由于空接口没有方法,任何类型都实现了它。这使得它对于需要处理未知类型值的函数非常有用。然而,要使用底层具体的(concrete)值,你需要进行类型断言(type assertion)或类型开关(type switch)。

func describe(i interface{}) {
fmt.Printf("Value: %v, Type: %T\n", i, i)
}
// In main():
// describe(42)
// describe("hello")
// describe(true)

在使用接口值时,你通常需要访问底层具体的类型或其特定的方法。这可以通过类型断言(type assertion)或类型开关(type switch)来完成。

类型断言(Type Assertion):value, ok := i.(ConcreteType) 检查接口值 i 是否持有具体的类型 ConcreteType,并将其赋给 value。如果断言成功,ok 为 true。

类型开关(Type Switch):switch v := i.(type) { case ConcreteType1: ... case ConcreteType2: ... } 允许连续进行多个类型断言。

接口是 Go 设计的基石,它使得系统灵活且可扩展。它们在标准库中被广泛使用,并且是编写地道的(idiomatic)Go 代码的关键。