Golang接口和反射 | 青训营笔记

75 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第10天

Golang接口和反射

interface

在Go中,interface是一种抽象的类型,更具体地说它是一组方法的集合,当一个类型为接口中的所有方法提供定义时,它被称为实现该接口。

如果一个动物走起路来像鸭子,叫声像鸭子,那么可以将它视为鸭子,在Go中,我们根据类型可以执行的操作而不是其所能容纳的数据类型来设计抽象

例如,我们通过定义animal的interface来设计其抽象,而创建几个实现该接口的类型:

type Animal interface {
	Speak() string
}

type Dog struct {
}

func (d Dog) Speak() string {
	return "Woof!"
}

type Cat struct {
}

func (c Cat) Speak() string {
	return "Meow!"
}

func main() {
	animals := []Animal{Dog{}, Cat{}}
	for _, animal := range animals {
		fmt.Println(animal.Speak())
	}
}

interface{}

interface{} 类型,空接口,所以所有类型都至少实现了 0 个方法,所以 所有类型都实现了空接口。这意味着,如果您编写一个函数以 interface{} 值作为参数,那么您可以为该函数提供任何值。

如fmt.Println接收的参数a

func Println(a ...any) (n int, err error) {
	return Fprintln(os.Stdout, a...)
}

// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}

reflect

当我们用一个interface接收到不同的类型的参数时,有时需要根据参数的不同类型做不同的处理。 如:

func checkType(into interface{}) {
	switch into.(type) {
	case int:
		fmt.Println("int")
	case bool:
		fmt.Println("bool")
	case float64:
		fmt.Println("float64")
	}
}

但这些类型可能没有确定的表示方式,或者是在我们设计该函数的时候这些类型可能还不存在。

我们就需要反射:

	default:
		fmt.Println(reflect.TypeOf(into))

反射是由 reflect 包提供的。它定义了两个重要的类型,Type 和 Value。一个 Type 表示一个Go类型。它是一个接口。

函数 reflect.TypeOf 接受任意的 interface{} 类型,并以 reflect.Type 形式返回其动态类型。

在格式化输出时,则可以使用%T来打印参数的类型

而reflect.Value则是该类型的值,值有不同的种类:

const (
	Invalid Kind = iota
	Bool
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Uintptr
	Float32
	Float64
	Complex64
	Complex128
	Array
	Chan
	Func
	Interface
	Map
	Pointer
	Slice
	String
	Struct
	UnsafePointer
)

反射三定律:

  • 反射可以将“接口类型变量”转换为“反射类型对象”
  • 反射可以将“反射类型对象”转换为“接口类型变量”
  • 如果要修改“反射类型对象”,其值必须是“可写的”(settable)

反射缺点:

  • 代码可读性降低
  • 编译器不能提前发现类型错误
  • 性能慢一到两个数量级