Go语言之初识接口

题图

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》

喜欢或者帮助到了你,点个赞吧。

你可能感兴趣的:(Go语言之初识接口)