packagemainimport"fmt"funcmain() {var i int=9var f float64 f =float64(i) fmt.Printf("%T, %v\n", f, f) f =10.8 a :=int(f) fmt.Printf("%T, %v\n", a, a)// s := []int(i)}
上面的代码里,我定义了一个 int 型和 float64 型的变量,尝试在它们之前相互转换,结果是成功的:int 型和 float64 是相互兼容的。
如果我把最后一行代码的注释去掉,编译器会报告类型不兼容的错误:
cannot convert i (type int) to type []int
断言
前面说过,因为空接口 interface{} 没有定义任何函数,因此 Go 中所有类型都实现了空接口。当一个函数的形参是 interface{},那么在函数中,需要对形参进行断言,从而得到它的真实类型。
packagemainimport"fmt"typeStudentstruct { Name string Age int}funcmain() {var i interface{} =new(Student) s := i.(Student) fmt.Println(s)}
运行一下:
panic: interface conversion: interface {} is *main.Student, not main.Student
直接 panic 了,这是因为 i 是 *Student 类型,并非 Student 类型,断言失败。这里直接发生了 panic,线上代码可能并不适合这样做,可以采用“安全断言”的语法:
funcmain() {var i interface{} =new(Student) s, ok := i.(Student)if ok { fmt.Println(s) }}
这样,即使断言失败也不会 panic。
断言其实还有另一种形式,就是用在利用 switch 语句判断接口的类型。每一个 case 会被顺序地考虑。当命中一个 case 时,就会执行 case 中的语句,因此 case 语句的顺序是很重要的,因为很有可能会有多个 case 匹配的情况。
代码示例如下:
funcmain() {//var i interface{} = new(Student)//var i interface{} = (*Student)(nil)var i interface{} fmt.Printf("%p%v\n", &i, i)judge(i)}funcjudge(v interface{}) { fmt.Printf("%p%v\n", &v, v)switch v := v.(type) {casenil: fmt.Printf("%p%v\n", &v, v) fmt.Printf("nil type[%T] %v\n", v, v)caseStudent: fmt.Printf("%p%v\n", &v, v) fmt.Printf("Student type[%T] %v\n", v, v)case*Student: fmt.Printf("%p%v\n", &v, v) fmt.Printf("*Student type[%T] %v\n", v, v)default: fmt.Printf("%p%v\n", &v, v) fmt.Printf("unknow\n") }}typeStudentstruct { Name string Age int}
main 函数里有三行不同的声明,每次运行一行,注释另外两行,得到三组运行结果:
// --- var i interface{} = new(Student)
0xc4200701b0 [Name: ], [Age: 0]
0xc4200701d0 [Name: ], [Age: 0]
0xc420080020 [Name: ], [Age: 0]
*Student type[*main.Student] [Name: ], [Age: 0]
// --- var i interface{} = (*Student)(nil)
0xc42000e1d0 <nil>
0xc42000e1f0 <nil>
0xc42000c030 <nil>
*Student type[*main.Student] <nil>
// --- var i interface{}
0xc42000e1d0 <nil>
0xc42000e1e0 <nil>
0xc42000e1f0 <nil>
nil type[<nil>] <nil>
对于第一行语句:
var i interface{} =new(Student)
i 是一个 *Student 类型,匹配上第三个 case,从打印的三个地址来看,这三处的变量实际上都是不一样的。在 main 函数里有一个局部变量 i;调用函数时,实际上是复制了一份参数,因此函数里又有一个变量 v,它是 i 的拷贝;断言之后,又生成了一份新的拷贝。所以最终打印的三个变量的地址都不一样。
对于第二行语句:
var i interface{} = (*Student)(nil)
这里想说明的其实是 i 在这里动态类型是 (*Student), 数据为 nil,它的类型并不是 nil,它与 nil 作比较的时候,得到的结果也是 false。