packagemainimport"fmt"var i =5var str ="ABC"typePersonstruct { name string age int}typeAnyinterface{}funcmain() {var val Any val =5 fmt.Printf("val has the value: %v\n", val) val = str fmt.Printf("val has the value: %v\n", val) pers1 :=new(Person) pers1.name ="Rob Pike" pers1.age =55 val = pers1 fmt.Printf("val has the value: %v\n", val)switch t := val.(type) {caseint: fmt.Printf("Type int %T\n", t)casestring: fmt.Printf("Type string %T\n", t)casebool: fmt.Printf("Type boolean %T\n", t)case*Person: fmt.Printf("Type pointer to Person %T\n", t)default: fmt.Printf("Unexpected type %T", t) }}
输出:
val has the value: 5
val has the value: ABC
val has the value: &{Rob Pike 55}
Type pointer to Person *main.Person
在上面的例子中,接口变量 val 被依次赋予一个 int,string 和 Person 实例的值,然后使用 type-switch 来测试它的实际类型。每个 interface {} 变量在内存中占据两个字长:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针。
packagemainimport"fmt"typespecialStringstringvar whatIsThis specialString="hello"funcTypeSwitch() { testFunc :=func(anyinterface{}) {switch v :=any.(type) {casebool: fmt.Printf("any %v is a bool type", v)caseint: fmt.Printf("any %v is an int type", v)casefloat32: fmt.Printf("any %v is a float32 type", v)casestring: fmt.Printf("any %v is a string type", v)casespecialString: fmt.Printf("any %v is a special String!", v)default: fmt.Println("unknown type!") } }testFunc(whatIsThis)}funcmain() {TypeSwitch()}
输出:
any hello is a special String!
练习 11.9 simple_interface3.go:
继续 练习11.2,在它中添加一个 gI 函数,它不再接受 Simpler 类型的参数,而是接受一个空接口参数。然后通过类型断言判断参数是否是 Simpler 类型。最后在 main 使用 gI 取代 fI 函数并调用它。确保你的代码足够安全。
11.9.2 构建通用类型或包含不同类型变量的数组
在 7.6.6 中我们看到了能被搜索和排序的 int 数组、float 数组以及 string 数组,那么对于其他类型的数组呢,是不是我们必须得自己编程实现它们?
现在我们知道该怎么做了,就是通过使用空接口。让我们给空接口定一个别名类型 Element:type Element interface{}
然后定义一个容器类型的结构体 Vector,它包含一个 Element 类型元素的切片:
typeVectorstruct { a []Element}
Vector 里能放任何类型的变量,因为任何类型都实现了空接口,实际上 Vector 里放的每个元素可以是不同类型的变量。我们为它定义一个 At() 方法用于返回第 i 个元素:
func (p *Vector) At(i int) Element {return p.a[i]}
var dataSlice []myType=FuncReturnSlice()var interfaceSlice []interface{} =make([]interface{}, len(dataSlice))for i, d :=range dataSlice { interfaceSlice[i] = d}
packagemainimport"fmt"typeNodestruct { le *Node data interface{} ri *Node}funcNewNode(left, right *Node) *Node {return&Node{left, nil, right}}func (n *Node) SetData(data interface{}) { n.data = data}funcmain() { 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\n", root) // Output: &{0x125275f0 root node 0x125275e0}}
11.9.5 接口到接口
一个接口的值可以赋值给另一个接口变量,只要底层类型实现了必要的方法。这个转换是在运行时进行检查的,转换失败会导致一个运行时错误:这是 Go 语言动态的一面,可以拿它和 Ruby 和 Python 这些动态语言相比较。
假定:
var ai AbsInterface// declares method Abs()typeSqrInterfaceinterface {Sqr() float}var si SqrInterfacepp :=new(Point) // say *Point implements Abs, Sqrvar empty interface{}
那么下面的语句和类型断言是合法的:
empty = pp // everything satisfies emptyai = empty.(AbsInterface) // underlying value pp implements Abs()// (runtime failure otherwise)si = ai.(SqrInterface) // *Point has Sqr() even though AbsInterface doesn’tempty = si // *Point implements empty set// Note: statically checkable so type assertion not necessary.
下面是函数调用的一个例子:
typemyPrintInterfaceinterface {print()}funcf3(x myInterface) { x.(myPrintInterface).print() // type assertion to myPrintInterface}
x 转换为 myPrintInterface 类型是完全动态的:只要 x 的底层类型(动态类型)定义了 print 方法这个调用就可以正常运行。