惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
量子位
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
V
Visual Studio Blog
雷峰网
雷峰网
T
Tailwind CSS Blog
宝玉的分享
宝玉的分享
Blog — PlanetScale
Blog — PlanetScale
有赞技术团队
有赞技术团队
博客园 - 叶小钗
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
U
Unit 42
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
小众软件
小众软件
阮一峰的网络日志
阮一峰的网络日志
Y
Y Combinator Blog

蛮荆

如何获取更多的免费服务器 Kubernetes 调度器队列 - 设计与实现 Kubernetes 调度器 - 核心流程 Kubernetes Networking Model & CNI Kubernetes 控制器管理总结 Kubernetes CronJob 设计与实现 Kubernetes Job 设计与实现 Kubernetes HPA 设计与实现 Kubernetes Deployment 滚动更新实现原理 Kubernetes GC 设计与实现 Kubernetes Pod 驱逐 - 设计与实现 Kubernetes Daemonset 设计与实现 Kubernetes ReplicaSet 设计与实现 Kubernetes EndPoint 设计与实现 Kubernetes Informer 设计与实现 降本增效之应用优化 (三) 日志存储与检索 Kubernetes Pod 设计与实现 - 创建流程 Kubernetes 探针设计与实现 Unix 编程艺术名句摘录 Kubernetes - CRI 概述 Golang 编译速度为什么这么快? Kubernetes Pod 设计与实现 - Pause 容器 Kubernetes - kube-proxy 代理模式工程优化 Kubernetes 应用最佳实践 - 优雅关闭长连接 Kubernetes Service 类型和会话亲和性 Kubernetes 为什么需要 Ingress Kubernetes 架构 - 控制平面和数据平面 降本增效之应用优化 (二) 大报表 Go 语言如何获取 CPU 利用率 降本增效之应用优化 (一) Redis
Go 如何实现 implements 功能
2023-01-31 · via 蛮荆

2023-01-31 Golang OOP

概述

Go 语言中检测一个类型是否实现了某个接口,通常分为两类机制: 编译期间运行期间

编译期间

顾名思义,编译期间检测就是通过静态分析来确定类型是否实现了某个接口,如果检测不通过,则编译过程报错并退出。 通常将这种方式称为 接口完整性检查

接口完整性检查

类型未实现接口

package main

type Person interface {
	Name() string
	Age() int
}

type Martian struct {
}

func main() {
	// 强制类型 Martian 必须实现接口 Person 的所有方法
	var _ Person = (*Martian)(nil)

	// 1. 声明一个 _ 变量 (不使用)
	// 2. 把一个 nil 转换为 (*Martian),然后再转换为 Person
	// 3. 如果 Martian 没有实现 Person 的全部方法,则转换失败,编译器报错
}

运行代码

$ go run main.go
## 输出如下 
cannot use (*Martian)(nil) (value of type *Martian) as type Person in variable declaration:
        *Martian does not implement Person (missing Age method)

从输出的结果中可以看到,Martian 并没有实现 Person 接口,所以报错了。下面我们为 Martian 实现 Person 接口。

类型实现了接口

package main

import "fmt"

type Person interface {
	Name() string
	Age() int
}

type Martian struct {
}

// 实现 Name 方法
func (m *Martian) Name() string {
	return "martian"
}

// 实现 Age 方法
func (m *Martian) Age() int {
	return -1
}

func main() {
	// 此时 Martian 已实现了 Person 的全部方法
	var _ Person = (*Martian)(nil)

	m := &Martian{}
	fmt.Printf("name = %s, age = %d\n", m.Name(), m.Age())
}

运行代码

$ go run main.go
## 输出如下 
name = martian, age = -1

从输出的结果中可以看到,运行成功,Martian 已经实现了 Person 接口的全部方法。

运行期间

运行期间的检测方式主要有 类型断言反射

类型断言

类型未实现接口

package main

import "fmt"

type Person interface {
	Name() string
	Age() int
}

type Martian struct {
}

func main() {
	// 变量必须声明为 interface 类型
	var m interface{}
	m = &Martian{}
	if v, ok := m.(Person); ok {
		fmt.Printf("name = %s, age = %d\n", v.Name(), v.Age())
	} else {
		fmt.Println("Martian does not implements Person")
	}
}

运行代码

$ go run main.go
## 输出如下 
Martian does not implements Person

下面我们为 Martian 实现 Person 接口。

类型实现了接口

package main

import "fmt"

type Person interface {
	Name() string
	Age() int
}

type Martian struct {
}

func (m *Martian) Name() string {
	return "martian"
}

func (m *Martian) Age() int {
	return -1
}

func main() {
	// 变量必须声明为 interface 类型
	var m interface{}
	m = &Martian{}
	if v, ok := m.(Person); ok {
		fmt.Printf("name = %s, age = %d\n", v.Name(), v.Age())
	}
}

运行代码

$ go run main.go
## 输出如下 
name = martian, age = -1

从输出的结果中可以看到,运行成功,Martian 已经实现了 Person 接口的全部方法。

反射

通过 reflect 包提供的 API 来判断类型是否实现了某个接口。

类型未实现接口

package main

import (
	"fmt"
	"reflect"
)

type Person interface {
	Name() string
	Age() int
}

type Martian struct {
}

func main() {
	// 获取 Person 类型
	p := reflect.TypeOf((*Person)(nil)).Elem()

	// 获取 Martian 结构体指针类型
	m := reflect.TypeOf(&Martian{})

	// 判断 Martian 结构体类型是否实现了 Person 接口
	fmt.Println(m.Implements(p))
}

运行代码

$ go run main.go
## 输出如下 
false

下面我们为 Martian 实现 Person 接口。

类型实现了接口

package main

import (
	"fmt"
	"reflect"
)

type Person interface {
	Name() string
	Age() int
}

type Martian struct {
}

func (m *Martian) Name() string {
	return "martian"
}

func (m *Martian) Age() int {
	return -1
}

func main() {
	// 获取 Person 类型
	p := reflect.TypeOf((*Person)(nil)).Elem()

	// 获取 Martian 结构体指针类型
	m := reflect.TypeOf(&Martian{})

	// 判断 Martian 结构体类型是否实现了 Person 接口
	fmt.Println(m.Implements(p))
}

运行代码

$ go run main.go
## 输出如下 
true

小结

Go 的接口实现检测机制分为 编译期间运行期间,其中编译期间的检测方式是 接口完整性检查, 而运行期间的检测方式有两种: 类型断言反射,一般情况尽量使用类型断言,这样可以避免反射带来的性能损耗。 文中提到的几种检测方式语法都比较新 (nan) 奇 (kan) ,读者可以参考代码的注释部分帮助理解。