如Go method中提及,Golang没有明确支持多态,但是通过其他手段可以实现类似C++中的多态特性,即本文中即将介绍的Go interface功能。
interface定义
interface(接口)是golang最重要的特性之一,Interface类型可以定义一组方法,但是这些不需要实现。请注意:此处限定是一组方法,既然是方法,就不能是变量;而且是一组,表明可以有多个方法。再多声明一点,interface本质上是一种类型,确切的说,是指针类型,此处暂且不多表,后文中自然能体会到。
interface是为实现多态功能,多态是指代码可以根据类型的具体实现采取不同行为的能力。如果一个类型实现了某个接口,所有使用这个接口的地方,都可以支持这种类型的值。
type 接口名称 interface {
method1(参数列表) 返回值列表
method2(参数列表) 返回值列表
...
methodn(参数列表) 返回值列表
}
接口通常以er作为名称后缀,方法名是声明组成部分,但参数名可不同或省略。如果接口没有任何方法声明,那么就是一个空接口(interface{}),它的用途类似面向对象里的根类型Object,可被赋值为任何类型的对象。接口变量默认值是nil。如果实现接口的类型支持,可做相等运算。
func main() {
var t1,t2 interface{}
println(t1==nil,t1==t2)
t1,t2=100,100
println(t1==t2)
t1,t2=map[string]int{},map[string]int{}
println(t1==t2)
}
输出为
true true
true
panic:runtime error:comparing uncomparable type map[string]int
此外,还可以像匿名字段那样,嵌入其他接口。目标类型方法集中必须拥有包含嵌入接口方法在内的全部方法才算实现了该接口。
type stringer interface{
string()string
}
type tester interface{
stringer // 嵌入其他接口
test()
}
type data struct{}
func(*data)test() {
}
func(data)string()string{
return""
}
func main() {
var d data
var t tester= &d
t.test()
println(t.string())
}
interface应用场景
类型转换
类型推断可将接口变量还原为原始类型,或用来判断是否实现了某个更具体的接口类型。
type data int
func(d data)String()string{
return fmt.Sprintf("data:%d",d)
}
func main() {
var d data=15
var x interface{} =d
if n,ok:=x.(fmt.Stringer);ok{ // 转换为更具体的接口类型
fmt.Println(n)
}
if d2,ok:=x.(data);ok{ // 转换回原始类型
fmt.Println(d2)
}
e:=x.(error) // 错误:main.data is not error
fmt.Println(e)
}
输出为:
data:15
data:15
panic:interface conversion:main.data is not error:missing method Error
但是此处会触发panic
,使用ok-idiom模式,即便转换失败也不会引发panic。还可用switch语句在多种类型间做出推断匹配,这样空接口就有更多发挥空间。
func main() {
var x interface{} =func(x int)string{
return fmt.Sprintf("d:%d",x)
}
switch v:=x.(type) { // 局部变量v是类型转换后的结果
case nil:
println("nil")
case*int:
println(*v)
case func(int)string:
println(v(100))
case fmt.Stringer:
fmt.Println(v)
default:
println("unknown")
}
}
输出为:
d:100
实现多态功能
多态功能是interface实现的重要功能,也是Golang中的一大行为特色,其多态功能一般要结合Go method实现,作为函数参数可以容易的实现多台功能。
package main
import "fmt"
// notifier是一个定义了通知类行为的接口
type notifier interface {
notify()
}
// 定义user及user.notify方法
type user struct {
name string
email string
}
func (u *user) notify() {
fmt.Printf("Sending user email to %s<%s>\n",
u.name,
u.email)
}
// 定义admin及admin.notify方法
type admin struct {
name string
email string
}
func (a *admin) notify() {
fmt.Printf("Sending admin email to %s<%s>\n",
a.name,
a.email)
}
func main() {
// 创建一个user值并传给sendNotification
bill := user{"Bill", "bill@email.com"}
sendNotification(&bill)
// 创建一个admin值并传给sendNotification
lisa := admin{"Lisa", "lisa@email.com"}
sendNotification(&lisa)
}
// sendNotification接受一个实现了notifier接口的值
// 并发送通知
func sendNotification(n notifier) {
n.notify()
}
上述代码中实现了一个多态的例子,函数sendNotification
接受一个实现了notifier接口的值作为参数。既然任意一个实体类型都能实现该接口,那么这个函数可以针对任意实体类型的值来执行notify
方法,调用notify
时,会根据对象的实际定义来实现不同的行为,从而实现多态行为。