Go 语言中的 interface

2016-07-26 Klaus Ma 更多博文 » 博客 » GitHub »

原文链接 http://www.k8s.tips/tech/2016/07/26/ifce_in_go/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


最近几天在读Kubernetes的代码,涉及到不少Go语言中需要注意的点;这里记录一下对这些功能的理解:

函数实现

在类定义方面,Go语言通过结构体+函数的形式提供了类的实现机制;比较像早期的g++的实现方式。以下面这个类定义为例:

{% highlight go %}type MyClass struct { A int }

func (mc *MyClass) PrintInfo() { mc.A++; fmt.Printf("%d\n", mc.A); } {% endhighlight %}

MyClass只定义了一个结构体,而 func (mc *MyClass) PrintInfo() 则定义了该结构体的一个方法。在早期的C++相关书籍中经常会提及这种C++编译方式:在C++的类函数中,隐式的传入该类的数据部分;从而实现成员函数的功能。不知是否有意为之,Go语言将这种方式显示的表达给程序员;如果没有之前c++相关的知识,理解进来还是有点困难的。另外,也正是由于将成员函数直接表达为函数+数据的形式,编译器才可以相对比较简单对函数签名进行比较;为后面接口的实现提供了可能。

另外,由于成员函数是数据+函数的形式,数据的传入方式的不同就会有不同的结果:

  • 当数据以指针的形式传入的时候,类的成员变量就是被修改,如:

{% highlight go %}func (mc *MyClass) PrintInfo() { mc.A++; fmt.Printf("%d\n", mc.A); } {% endhighlight %}

  • 当数据以传值的方式传入时,类的成员变量的值不会被修改;从而达到只读的效果,如:

{% highlight go %}func (mc MyClass) PrintInfo() { mc.A++; fmt.Printf("%d\n", mc.A); } {% endhighlight %}

使用接口

与其它语言一样,Go语言中的接口定义了一个类必需要有的方法;同时,Go语言中的接口还为类实例的访问提供了方便。在上面的章节提到,Go语言将类数据与函数分开;因此接口实际提供了两个指针,分别针对数据和函数:

interface

同时,接口中还保存了数据的类型:值 还是 指针;在使用时,Go语言可以帮助程序员自动解引用。下面的程序段展现了一种接口和类定义的使用方式:

{% highlight go %}type MyInterface interface { PrintInfo() }

type MyClass struct { A int }

func NewMyClass() MyInterface { return &MyClass{}; }

func (mc *MyClass) PrintInfo() { mc.A++; fmt.Printf("%d\n", mc.A); } {% endhighlight %}

如果 NewMyClass 的返回值为 MyClass,则类型一定要匹配:值 或 指针。但是,如果返回值为 MyInterface,则值和指针都可以返回,Go语言会帮助程序员进行解引用。