-
Notifications
You must be signed in to change notification settings - Fork 174
Description
注:所有内容摘录自 Go 语言接口的实现原理,为该文章的读书笔记。
什么是接口
在计算机科学中,接口是系统中组件之间的边界或者叫接缝,不同的组件能够在边界上交换信息。接口的本质是引入一个新的中间层,调用方可以通过接口与具体实现分离,解除上下游的耦合,上层的模块不再需要依赖下层的具体模块,只需要依赖一个约定好的接口。
Java语言中的接口
很多面向对象语言都有接口这一概念,例如 Java 和 C#。Java 的接口不仅可以定义方法签名,还可以定义变量,这些定义的变量可以直接在实现接口的类中使用。
public interface MyInterface {
public String hello = "Hello";
public void sayHello();
}
上述代码定义了一个必须实现的方法 sayHello 和一个会注入到实现类的变量 hello。在下面的代码中,MyInterfaceImpl
实现了 MyInterface
接口:
public class MyInterfaceImpl implements MyInterface {
public void sayHello() {
System.out.println(MyInterface.hello);
}
}
Java 中的类必须通过上述方式显式地声明实现的接口,但是在 Go 语言中实现接口使用的是隐式实现。
Go 语言中的接口
Go 语言里一个接口类型是一组方法签名的集合。一个接口类型的变量可以保存任何实现了这些方法的值。
定义接口需要使用 interface 关键字,在接口中我们只能定义方法签名,不能包含成员变量,一个常见的 Go 语言接口是这样的:
type error interface {
Error() string
}
如果一个类型需要实现 error 接口,那么它只需要实现 Error() string 方法,下面的 RPCError 结构体就是 error 接口的一个实现:
type RPCError struct {
Code int64
Message string
}
func (e *RPCError) Error() string {
return fmt.Sprintf("%s, code=%d", e.Message, e.Code)
}
上述代码根本就没有 error 接口的影子,这是因为 Go 语言中接口的实现都是隐式的,我们只需要实现 Error() string 方法就实现了 error 接口。Go 语言实现接口的方式与 Java 完全不同:
- 在 Java 中:实现接口需要显式地声明接口并实现所有方法;
- 在 Go 中:实现接口的所有方法就隐式地实现了接口;
使用上述 RPCError 结构体时并不关心它实现了哪些接口,Go 语言只会在传递参数、返回值以及变量赋值时才会对某个类型是否实现接口进行检查。
func main() {
var rpcErr error = NewRPCError(400, "unknown err") // 赋值给变量 rpcErr 时进行类型检查,看是否实现了 error 接口
err := AsErr(rpcErr) // 给函数传递参数时进行类型检查
println(err)
}
func NewRPCError(code int64, msg string) error {
return &RPCError{ // 函数返回值时进行类型检查
Code: code,
Message: msg,
}
}
func AsErr(err error) error {
return err
}
Go 语言在编译期间对代码进行类型检查,上述代码总共触发了三次类型检查。