Go语言之初识接口

作者: 有财君 | 来源:发表于2019-05-10 23:38 被阅读578次
    题图

    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", "lee@126.com"}
        a := admin{"zhang", "zhang@126.com", "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", "lee@126.com"}
        a := admin{"zhang", "zhang@126.com", "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", "lee@126.com"}
        a := admin{"zhang", "zhang@126.com", "delete"}
        //虽然方法的接收者定义为指针,但是直接传值也无所谓,这是方法集规则
        u.notify()
        a.notify()
    }
    

    但是,从接收者的角度来看,就变成了这样:

    Method Receivers Value
    (t T) T and *T
    (t *T) *T

    这样,用指针作为接收者实现的接口,能接收的只能指针了。

    3. 小结

    其实接口只是Go语言的类型系统中的一种,不过可以借助接口实现多态,这是很不错的一种方式,虽然和Java差别有点大。

    不过已经掌握了一门编程语言的人,在遇到了一个喜欢的语言的时候,想学习就会很快了。

    参考:《Go IN ACTION》

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

    相关文章

      网友评论

        本文标题:Go语言之初识接口

        本文链接:https://www.haomeiwen.com/subject/qpgzoqtx.html