1. 从Java说起接口
Java的接口是一种很好的东西,一定程度上解决了Java只允许单根继承的限制。我们可以认为接口是一种约定,它规定了一个类应该具有什么样的能力。
很久以来,只要掌握了接口,写代码都会变得很容易,这就叫做“面向接口编程”。
我个人就很喜欢用接口。
比如我们学习Java的时候,很多入门教材上都会举汽车作为继承的例子,汽车作为父类,下面会有小汽车,公交车,跑车或者越野车种种子类。那么这个时候我就可以实现一个汽车的接口:
public interface Car {
void run();
}
之后所有的子类都可以实现这个接口,比如公交车:
public class Bus implement Car {
@Override
public void run() {
...
}
}
接口最大的作用之一就是实现多态,这是面向对象的一大特性:
Car bus = new Bus();
多态就这么自然而然的实现了。
2. Go的接口
接口为了实现多态,这一点其实在Go上也不例外。先上一段代码,准备说明问题:
package main
import "fmt"
//接口倒是很像Java,声明了方法
type notifier interface {
notify()
}
//定义普通用户
type user struct {
name string
email string
}
//定义管理员用户,还有个权限域
type admin struct {
name string
email string
privilege string
}
func (u *user) notify() {
fmt.Println("name:", u.name, "send mail to: ", u.email)
}
func (a *admin) notify() {
fmt.Println("name: ", a.name, "send mail to: ", a.email, "has privileges: ", a.privilege)
}
func sendNotify(n notifier) {
n.notify()
}
func main() {
u := user{"lee", "[email protected]"}
a := admin{"zhang", "[email protected]", "delete"}
sendNotify(&u)
sendNotify(&a)
}
下面说明一下:
首先我声明了一个接口notifier,接口有一个notify的方法;
接下来我利用结构体定义两个类型user和admin;
接下来我声明了两个notify方法,不过就是接收者不同;
到此时只是实现了notify方法,但是还是看不出多态在哪里体现,下面先看看main函数;
u和a分别是user和admin类型,但是他们都能作为参数传给sendNotify函数;
sendNotify函数的入参是notifier接口类型。
到这里,我们大致可以理解为user和admin都实现了notifier接口,只是实现的方式和Java是有天壤之别的。
这里有个地方要特别注意一下,我在实现接口的时候,接收者定义了指针类型,那么只有指向这个类型(notifier)的指针才能实现对应的接口,因此sendNotify函数接收的参数一定是指针。
但是如果实现的时候改成这样:
func (u user) notify() {
fmt.Println("name:", u.name, "send mail to: ", u.email)
}
func (a admin) notify() {
fmt.Println("name: ", a.name, "send mail to: ", a.email, "has privileges: ", a.privilege)
}
就可以直接传值的方式了:
func main() {
u := user{"lee", "[email protected]"}
a := admin{"zhang", "[email protected]", "delete"}
sendNotify(u)
sendNotify(a)
}
这就牵扯到方法集规则的问题了,方法集规则如下定义:
Values | Method Receivers |
---|---|
T | (t T) |
*T | (t T)and(t *T) |
这就能解释这种代码为什么对:
func (u *user) notify() {
fmt.Println("name:", u.name, "send mail to: ", u.email)
}
func (a *admin) notify() {
fmt.Println("name: ", a.name, "send mail to: ", a.email, "has privileges: ", a.privilege)
}
func main() {
u := user{"lee", "[email protected]"}
a := admin{"zhang", "[email protected]", "delete"}
//虽然方法的接收者定义为指针,但是直接传值也无所谓,这是方法集规则
u.notify()
a.notify()
}
但是,从接收者的角度来看,就变成了这样:
Method Receivers | Value |
---|---|
(t T) | T and *T |
(t *T) | *T |
这样,用指针作为接收者实现的接口,能接收的只能指针了。
3. 小结
其实接口只是Go语言的类型系统中的一种,不过可以借助接口实现多态,这是很不错的一种方式,虽然和Java差别有点大。
不过已经掌握了一门编程语言的人,在遇到了一个喜欢的语言的时候,想学习就会很快了。
参考:《Go IN ACTION》
喜欢或者帮助到了你,点个赞吧。