接口
接口的基本使用
Go语言中的接口很简单,通过interface
就可以定义一个接口,在接口中定义的方法和java一样,是没有实现的,只做声明。接口的名字一般是方法名加er
组成。不适合用er
的,也可以用able
结束。
type Flayer interface {
fly()
}
如上我们定义了一个player
的接口。Go语言中没有implements
的关键字,如果要实现一个接口,参考下面的例子:
type bird struct {
}
// 按照接口中fly方法的返回值、参数列表做声明,函数的接受者定义为bird类型的一个指针,就相当于bird实现了Player接口
func (bi *bird) fly() {
fmt.Println("bird fly...")
}
type plane struct {
}
func (bi *plane) fly() {
fmt.Println("plane fly...")
}
func main() {
flayer1 := new(bird)
flayer1.fly()
var flayer2 Flayer
flayer2 = new(plane)
flayer2.fly()
}
// 输出
bird fly...
plane fly...
接口类型的检测
因为接口是一个动态类型,就像上面的例子,有可能是bird类型,也有可能是plane类型。那么我们如何在运行时检测他到底是那种类型呢?代码如下:
func main() {
var flayer2 Flayer
flayer2 = new(plane)
flayer2.fly()
// ok是一个bool值 如果为true,则t是plane类型的值
if t, ok := flayer2.(*plane); ok {
fmt.Printf("%T", t)
}
if _, ok := flayer2.(*plane); ok {
fmt.Printf("%T", t)
}
}
// 输出:
bird fly...
plane fly...
*main.plane
除了上面的方式,也可以用type-switch
的方式去做,代码如下:
switch t := flayer2.(type) {
case *plane:
fmt.Printf("plane %T\n", t)
case *bird:
fmt.Printf("bird %T\n", t)
default:
fmt.Println("unknow")
}
如果我们不关注t的取值,那么上面的代码也可以简写成下面的样子:
switch flayer2.(type) {
case *plane:
// todo
case *bird:
// todo
default:
// todo
}
空接口
在C语言中,有一个函数malloc
函数用来开辟内存空间,参数是要开辟的空间大小,返回值是开辟出来的空间的地址。那么这个返回的地址是用来存什么类型的数据的呢?int类型,那返回值类型应该定义成int *p
,float类型,那返回值类型应该定义成float *p
。。。可是这个是确定不了的,是在用户使用的时候动态确定的,因此它的返回值类型不能是确定的,也应该是动态的。具体怎么表示呢?我们看看malloc
函数的声明void *__cdecl malloc(size_t _Size);
,他这里是用了void *
类型的指针来指向一个空类型,一个不确定类型。在使用的时候我们可以将他强制转换成任意一个确定的类型。比如void * p1 = malloc(100); int p2 = (int *)p1
。
在java中,类似的功能是通过Object
类来实现的,他是所有类型的基类,也就是说Object
类的对象可以指向任意类型的一个引用。在Ts中,类似的写法是Any
。那么在Go语言中,类似的功能则被叫做空接口。虽然换了一个概念,但是本质解决的问题是相同的。
Go语言中的空接口是接口类型的一种特殊形式,空接口没有任何方法,因此任何类型都无须实现空接口。换句话说,任何值都满足这个接口的需求,都可以看做实现了这个空接口。因此空接口类型可以保存任何值,也可以从空接口中取出原值。如下代码片段:
type Student struct {
name string
age int
score float32
}
// 定义一个空接口
type Any interface{}
func main() {
var val Any
val = 1 // 将int类型赋值给空接口
fmt.Printf("val has the value: %v\n", val)
val = "go" // 将string类型赋值给空接口
fmt.Printf("val has the value: %v\n", val)
stu := Student{"haxingyu", 12, 100.0}
val = stu // 将struct类型赋值给空接口
fmt.Printf("val has the value: %v\n", val)
switch t := val.(type) {
case int:
fmt.Printf("Type int %T\n", t)
case string:
fmt.Printf("Type int %T\n", t)
case Student:
fmt.Printf("Type int %T\n", t)
default:
fmt.Println("unknown")
}
}
// 输出:
val has the value: 1
val has the value: go
val has the value: {haxingyu 12 100}
Type int main.Student