Go 语言中的 interface

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

函数实现

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

type MyClass struct {
    A int
}

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

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

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

  • 当数据以指针的形式传入的时候,类的成员变量就是被修改,如:
func (mc *MyClass) PrintInfo() {
	mc.A++;
	fmt.Printf("%d\n", mc.A);
}
  • 当数据以传值的方式传入时,类的成员变量的值不会被修改;从而达到只读的效果,如:
func (mc MyClass) PrintInfo() {
	mc.A++;
	fmt.Printf("%d\n", mc.A);
}

使用接口

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

interface

同时,接口中还保存了数据的类型:值 还是 指针;在使用时,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);
}

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

Written on July 26, 2016