Skip to content

接口 #45

@kevinyan815

Description

@kevinyan815

注:所有内容摘录自 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 语言在编译期间对代码进行类型检查,上述代码总共触发了三次类型检查。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions