【Go Trick】2.接口实现确认

接口概述

Go语言中的接口定义了一组方法的集合,接口本身不能实例化,需要具体的类型实现。
一个类型可以实现多个接口,如果该类型的包含了一个接口的所有同名方法,它就会自动实现这个接口。(接口即合约)

首先,接口只能声明方法,不能提供方法的具体实现。一个具体的类型实现了接口的全部方法,它就可以被视作实现了这个接口,可以将地址赋值给该接口类型的变量,类似于C++的基类指针,这是Go实现多态的关键。

type Person interface {
	getName() string  // 接口声明的方法
}


type Student struct {
	name string
	age  int
}

// Student类实现了getName()方法,因此Student实现了Person接口
func (student *Student) getName() string {
	return student.name
}

func main() {
	var person Person = &Student{
		name: "Sheep",
		age:  18,
	}
	fmt.Println(person.getName())
}

如上,可以将Student取地址,赋值给person变量,通过person.getName()即可调用Student的getName()方法。
如果此时将StudentgetName()方法注释掉,IDE就会高亮显示错误,编译也会有响应报错

cannot use &Student{…} (value of type *Student) as Person value in variable declaration: *Student does not implement Person (missing method getName)

接口实现检查

由于在Go语言中接口是一种合约,并不会显式地进行检查,如Java中的implements关键字显式指定某个类实现某个接口,Go只会在结构体赋值给接口变量时才会进行判断。那么,如果代码中没有进行实际的多态操作,该怎么确保某个结构体实现特定的接口呢?在实际开发过程中,代码很庞杂,接口也可能有多个方法,难道只能一个个去比对方法名吗?
一个误区是,以为在结构体中包含匿名的接口类型成员变量就能显式指定接口的实现,如下

type Student struct {
	name string
	age  int
	Person
}

其实并非如此,这只是Go语言匿名嵌入的特性,代表Student结构体有一个匿名的Person成员,跟接口实现没有半毛钱关系。
正确的思路是,在定义完结构体之后,直接进行一次没有实际意义的强制类型转换不就好了?答案是这样

var _ Person = (*Student)(nil)

下面来解释一下这段代码,这就是Go编程常用到的接口检查器语法。var代表进行一次全局变量的定义,_ Person意思是一个匿名的Person变量,等号右边则是对nil空值进行强制类型转换,变成指针*Student。这个语句能够编译成功的条件就是Person类型的变量能接受*Student类型的赋值,即Student实现了Person接口,而因为是定义了匿名变量,因此也不会对代码逻辑产生影响。
Go为了体现”接口即合约“的思想,让接口的实现变成隐式的,然而人们最终还是为了做显式确认,创造出了接口检查器语法,这是好是坏呢,Go语言的设计者乐见这一点吗?


【Go Trick】2.接口实现确认
https://sheep-in-box.github.io/2024/08/02/【Go-Trick】2-接口实现确认/
作者
Sheep
发布于
2024年8月2日
许可协议