Empty Interface in golang.
About 996 wordsAbout 3 min
Golang
2024-11-13
空接口的概念和作用。
空接口或者最小接口 不包含任何方法,它对实现不做任何要求:
type Any interface {}
可以给一个空接口类型的变量 var val interface {}
赋任何类型的值。
package main
import "fmt"
var i = 5
var str = "ABC"
type Person struct {
name string
age int
}
type Any interface{}
func main() {
var val Any
val = 5
fmt.Printf("val has the value: %v
", val)
val = str
fmt.Printf("val has the value: %v
", val)
pers1 := new(Person)
pers1.name = "Rob Pike"
pers1.age = 55
val = pers1
fmt.Printf("val has the value: %v
", val)
switch t := val.(type) {
case int:
fmt.Printf("Type int %T
", t)
case string:
fmt.Printf("Type string %T
", t)
case bool:
fmt.Printf("Type boolean %T
", t)
case *Person:
fmt.Printf("Type pointer to Person %T
", t)
default:
fmt.Printf("Unexpected type %T", t)
}
}
在上面的例子中,接口变量 val
被依次赋予一个 int
,string
和 Person
实例的值,然后使用 type-switch
来测试它的实际类型。每个 interface {}
变量在内存中占据两个字长:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针。
通用类型容器
给空接口定一个别名类型 Element
:type Element interface{}
,然后定义一个容器类型的结构体 Vector
,它包含一个 Element
类型元素的切片:
type Vector struct {
a []Element
}
Vector
里能放任何类型的变量,因为任何类型都实现了空接口,实际上 Vector
里放的每个元素可以是不同类型的变量。我们为它定义一个 At()
方法用于返回第 i
个元素:
func (p *Vector) At(i int) Element {
return p.a[i]
}
再定一个 Set()
方法用于设置第 i
个元素的值:
func (p *Vector) Set(i int, e Element) {
p.a[i] = e
}
Vector
中存储的所有元素都是 Element
类型,要得到它们的原始类型(unboxing:拆箱)需要用到类型断言。类型断言总是在运行时才执行,因此它会产生运行时错误。
数据复制
假设你有一个 myType
类型的数据切片,你想将切片中的数据复制到一个空接口切片中,类似:
var dataSlice []myType = FuncReturnSlice()
var interfaceSlice []interface{} = dataSlice
可惜不能这么做,编译时会出错:cannot use dataSlice (type []myType) as type []interface { } in assignment
。
原因是它们俩在内存中的布局是不一样的。必须使用 for-range
语句来一个一个显式地赋值:
var dataSlice []myType = FuncReturnSlice()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
interfaceSlice[i] = d
}
接口赋值
一个接口的值可以赋值给另一个接口变量,只要底层类型实现了必要的方法。这个转换是在运行时进行检查的,转换失败会导致一个运行时错误:这是 Go
语言动态的一面,可以拿它和 Ruby
和 Python
这些动态语言相比较。
var ai AbsInterface // declares method Abs()
type SqrInterface interface {
Sqr() float
}
var si SqrInterface
pp := new(Point) // say *Point implements Abs, Sqr
var empty interface{}
那么下面的语句和类型断言是合法的:
empty = pp // everything satisfies empty
ai = empty.(AbsInterface) // underlying value pp implements Abs()
// (runtime failure otherwise)
si = ai.(SqrInterface) // *Point has Sqr() even though AbsInterface doesn’t
empty = si // *Point implements empty set
// Note: statically checkable so type assertion not necessary.
下面是函数调用的一个例子:
type myPrintInterface interface {
print()
}
func f3(x myInterface) {
x.(myPrintInterface).print() // type assertion to myPrintInterface
}
x
转换为 myPrintInterface
类型是完全动态的:只要 x
的底层类型(动态类型)定义了 print
方法这个调用就可以正常运行(译注:若 x
的底层类型未定义 print
方法,此处类型断言会导致 panic
,最佳实践应该为 if mpi, ok := x.(myPrintInterface); ok { mpi.print() }
。
模板编程
package main
import "fmt"
type Node struct {
le *Node
data interface{}
ri *Node
}
func NewNode(left, right *Node) *Node {
return &Node{left, nil, right}
}
func (n *Node) SetData(data interface{}) {
n.data = data
}
func main() {
root := NewNode(nil, nil)
root.SetData("root node")
// make child (leaf) nodes:
a := NewNode(nil, nil)
a.SetData("left node")
b := NewNode(nil, nil)
b.SetData("right node")
root.le = a
root.ri = b
fmt.Printf("%v
", root) // Output: &{0x125275f0 root node 0x125275e0}
}