Skip to content

什么是接口 interface

接口是一个非常重要的概念, 它描述了一组抽象的规范, 而不提供具体的实现 对于项目而言会使得代码更加优雅可读, 对于开发者而言也会减少很多心智负担, 代码风格逐渐形成了规范, 于是就有了现在人们所推崇的面向接口编程

在 Go 语言中, 接口主要用来约束行为, 其次也可以用作其他方面

基本接口

声明接口: type 接口名称 interface{ 行为(参数 参数类型) ?可选返回值 }

go
package main

import "fmt"

// 声明接口
type Animal interface {
	breathe()
	eat(food string) string
}

// 声明结构体
type Dog struct {
	name string
}

// 虽然暂时没有用到这个方法, 但是必须实现,
// 否则就无法将 Dog 结构体传给 animalEatFood 方法作为参数
func (d Dog) breathe() {
	fmt.Println("狗需要呼吸")
}

func (d Dog) eat(food string) string {
	return d.name + "吃了" + food
}

// 使用接口: 动物吃东西方法
// 只要是实现了 Animal 这个接口的 结构体
// 都可以当作参数传入这个方法
func animalEatFood(animal Animal) {
	msg := animal.eat("鱼")
	fmt.Println(msg)
}

// 再声明一个接口体, 并且实现 Animal 接口
type Cat struct {
	name string
}

func (c Cat) breathe() {
	fmt.Println("猫需要呼吸")
}

func (c Cat) eat(food string) string {
	return c.name + "吃了" + food
}

func (c Cat) catchMouse() {
	fmt.Println("猫会抓老鼠")
}

func main() {
	d := Dog{"小狗"}
	animalEatFood(d)
	d.breathe()

	c := Cat{"小猫"}
	animalEatFood(c)
	c.catchMouse()
}

内置接口

  • compare: 可以比较相等的值的接口
  • any: 允许任意类型的接口

仅约束数据类型的接口

空接口

表示允许任何类型, any 类型的实现原理就是空接口

go
package main

func main() {
	// m := map[string]interface{}{}
	m := map[string]any{}
	m["name"] = "tom"
	m["age"] = 18
}

类型约束接口

只约束数据类型不约束行为(方法)

go
package main

import "fmt"

// 定义一个类型约束,只允许无符号整数类型
type UnsignedInteger interface {
	uint | uint8 | uint16 | uint32 | uint64
}

// 检查年龄是否有效,使用泛型函数
func isValidAge[T UnsignedInteger](age T) bool {
	// 由于是无符号整数,自然不能为负数
	// 这里只检查是否在合理的人类年龄范围内
	return age <= 150
}

// 计算物品数量总和
func sumCounts[T UnsignedInteger](counts []T) T {
	var total T
	for _, count := range counts {
		total += count
	}
	return total
}

func main() {
	// 年龄示例 - 不能为负数
	var age uint8 = 25
	fmt.Printf("年龄 %d 是否有效: %v\n", age, isValidAge(age))

	// 计算总和
	stock := []uint{10, 20, 30, 40}
	total := sumCounts(stock)
	fmt.Printf("总库存数量: %d\n", total)

	// 尝试使用有符号整数会编译失败
	// var negativeAge int = -5
	// isValidAge(negativeAge)
	// 负数导致编译错误:
	// int does not satisfy UnsignedInteger (int missing in uint | uint8 | uint16 | uint32 | uint64)
}

类型断言

类型断言通常用于判断某一接口类型的变量是否属于某一个类型

go
package main

import "fmt"

func main() {
	// 断言接口类型的变量
	var num any = 10 // => var num interface{} = 10
	value, isOk := num.(int)
	if isOk {
		fmt.Println(value, isOk)
	} else {
		fmt.Println("error type")
	}

	// 强行断言非接口类型的变量
	// 编译错误:
	// invalid operation: fnum (variable of type float64) is not an interface
	// var fnum float64 = 1.2
	// fvalue, isOk := fnum.(int)
}

类型判断

这个在 1.18 后不推荐了, 因为已经有泛型了, 不需要手动判断类型了

go
package main

import (
	"errors"
	"fmt"
)

// 使用泛型
func plus[T int | float64](a, b T) T {
	return a + b
}

// 1.18之前无法使用泛型
func plus2(a, b interface{}) (interface{}, error) {
	switch a.(type) { // 判断变量的类型
	case int:
		result := a.(int) + b.(int)
		return result, nil
	case float64:
		result := a.(float64) + b.(float64)
		return result, nil
	default:
		var zero interface{}
		return zero, errors.New("unsupported type")
	}
}

func main() {
	fmt.Println(plus(1, 2))     // 3
	fmt.Println(plus(1.2, 2.3)) // 3.5

	fmt.Println("-----------")

	fmt.Println(plus2(1, 2))     // 3 <nil>
	fmt.Println(plus2(1.2, 2.3)) // 3.5 <nil>
}

Released under the MIT License.