接口
使用了接口实现了多态与继承,我们也应该详细了解接口的使用方式。
接口(interface)类型是对其他类型行为的概括与抽象。接口定义了一组方法,但是不包含这些方法的具体实现。
本质上接口依旧是一个类型,确切的说,是指针类型。如果一个类型实现了某个接口,则所有使用这个接口的地方都支持这种类型的值。
需要注意的是,如果实现接口的类型支持相等运算,那么可以比较,否则会报错。示例如下:
func main() { var var1,var2 interface{} fmt.Println(var1==nil,var2==nil) var1,var2=6,8 fmt.Println(var1==var2) var1,var2=map[string]string{},map[string]string{} fmt.Println(var1==var2) }
运行之后,大家会发现,空接口变量默认值是nil。也就是第一个输出肯定是两个true。而数值不相等,第二个输出false。第三个因为map类型不支持相等运算,所以报错。
接口的赋值
Go语言的接口不支持直接实例化,但支持赋值操作,从而快速实现接口与实现类的映射。
接口赋值在Go语言中分为如下两种情况:
将实现接口的对象实例赋值给接口
将一个接口赋值给另一个接口。
将实现接口的对象实例赋值给接口
将指定类型的对象实例赋值给接口,要求该对象对应的类实现了接口要求的所有方法,否则就不能算实现了该接口。
type Number int func (x Number) Equal(i Number) bool { return x == i } func (x Number) LessThan(i Number) bool { return x < i } func (x Number) MoreThan(i Number) bool { return x > i } func (x *Number) Multiple(i Number) { *x = *x * i } func (x *Number) Divide(i Number) { *x = *x / i } type NumberI interface { Equal(i Number) bool LessThan(i Number) bool MoreThan(i Number) bool Multiple(x Number) Divide(x Number) } func main() { var x Number = 8 var y NumberI = &x fmt.Println(x) fmt.Println(y) }
这里,我们先定义了一个Number类型以及相关方法。按照Go语言的约定,Number类型实现了NumberI接口,接下来就可以将Num类型对应的对象实例赋值给Number接口。
为什么要将&x的指针赋值给接口变量呢?这是因为Go语言会根据下面这样的非指针成员方法:
func (x Number) Equal(i Number) bool
自动生成一个新的与之对应的指针成员方法:
func (x *Number) Equal(i Number) bool{ return (*x).Equal(i) }
这样一来,类型*Number就存在所有NumberI接口中声明的方法了。
将接口赋值给接口
在Go语言中,只要两个接口拥有相同的方法列表,则它们就是等同的,可以互相赋值。这里,我们直接将前面的三角形修改一下。
type Triangle struct { Bottom float32 Height float32 } type Area1 interface { Area(x,y float32) float32 } type Area2 interface { Area(x,y float32) float32 } func (a Triangle) Area(x,y float32) float32 { return x*y } func main() { f1 :=Triangle{2,3} var f2 Area1=f1 var f3 Area2=f2 fmt.Println(f3) }
如果接口Area1的方法列表是接口Area2 的方法列表的子集,则接口Area2可以赋值给接口Area1 。修改为:
type Area2 interface { Area(x, y float32) float32 Sum(x, y float32) float32 } func (a Triangle) Sum(x, y float32) float32 { return x + y } func main() { f1 := Triangle{2, 3} var f2 Area2 = f1 var f3 Area1 = f2 fmt.Println(f3) }
接口查询
接口查询是在程序运行时进行的,查询是否成功,也要在运行时才能够确定。示例如下:
//语法 if filew,ok:=fileWriter.(*File);ok{ //... } //示例 func main() { slice := make([]int, 0) slice = append(slice, 6, 7, 8) var I interface{} = slice if res, ok := I.([]int); ok { fmt.Println(res) fmt.Println(ok) } }
上面代码中的if语句会判断接口I所指向的对象是否是[]int类型,如果是,则输出切片中的元素。
通过使用”接口类型.(type)“形式,加上switch-case语句,可以判断接口存储的类型。示例如下:
func Len(array interface{}) int { var length int if array == nil { length = 0 } switch array.(type) { case []int: length = len(array.([]int)) case []string: length = len(array.([]string)) case []float32: length = len(array.([]float32)) default: length = 0 } fmt.Println(length) return length } func main() { slice := make([]int, 0) slice = append(slice, 6, 7, 8) var I interface{} = slice Len(I) }
接口的组合
在Go语言中,不仅结构体与结构体之间可以嵌套,接口与接口之间也可以嵌套创造出新的接口。一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。
如果接口的所有方法被实现,则这个接口中的所有嵌套接口的方法均可以被调用。接口的组合很简单,直接将接口名写入接口内部即可。另外,还可以在接口内部再定义自己的接口方法。示例如下:
type interface1 interface { PrintlnStr(s string)(a string) } type interface2 interface { PrintlnInt(s int)(a int) } type interface3 interface { interface1 interface2 }