Golang 接口类型

221 阅读3分钟

接口类型

Go 语言中的接口就是一组方法的签名,它是 Go 语言的重要组成部分,使用接口能够让我们更好地组织并写出易于测试的代码

在计算机科学中,接口是计算机系统中多个组件共享的边界,不同的组件能够在边界上交换信息;接口的本质就是引入一个新的中间层,调用方可以通过接口和具体实现分离,解除上下游的耦合,上层的模块不再需要依赖下层的具体模块,只需要依赖一个约定好的接口

隐式接口

定义接口需要使用 interface 关键字,在接口中我们只能定义方法签名,不能包含成员变量

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)
}

接口类型检查

func main() {
    // 将 *RPCError 类型的变量赋值给 error 类型的变量 rpcErr
    var rpcErr error = NewRPCError(400, "unknown err") 
    // 将 *RPCError 类型的变量 rpcErr 传递给签名中参数类型为 error 的 AsErr 函数
    err := AsErr(rpcErr)
    println(err) 
}

func NewRPCError(code int64, msg string) error {
    // 将 *RPCError 类型的变量从函数签名的返回值类型为 error 的 NewRPCError 函数中返回
    return &RPCError{
        Code:    code,
        Message: msg,
    }
}

func AsErr(err error) error {
    return err
}

类型转换

package main

func main() {
    type Test struct{}
    v := Test{}
    Print(v)
}

func Print(v interface{}) {
    println(v)
}

上述函数只接受 interface{} 类型的值,在调用 Print 函数时会将参数 v 进行类型转换,将原来的 Test 类型转换成 interfacce() 类型

指针和接口

type Cat struct {}

type Duck interface {
    Quack()
}

func (c  Cat) Quack {}  // 使用结构体实现接口
func (c *Cat) Quack {}  // 使用结构体指针实现接口

var d Duck = Cat{}      // 使用结构体初始化变量
var d Duck = &Cat{}     // 使用结构体指针初始化变量
\结构体实现接口结构体指针实现接口
结构体初始化变量通过不通过
结构体指针初始化变量通过通过

分析: Go 语言在传递参数时都是传值的 对于 &Cat{} 来说,意味着拷贝了一个新的 &Cat{} 指针,该指针与原来的指针指向一个相同且唯一的结构体,所以编译器可以隐式的对变量解引用(dereference)获取指针指向的结构体 对于 Cat{} 来说,意味着 Quack 方法会接受一个全新的 Cat{} ,因为方法的参数是 *Cat,编译器不会无中生有创建一个新的指针;即使编译器可以创建新指针,这个指针指向的也不是最初调用该方法的结构体

动态派发

动态派发(Dynamic dispatch)是在运行期间选择具体多态操作(方法或者函数)执行的过程,它是一种在面向对象语言中常见的特性。Go 语言对接口的引入带来了动态派发这个特性 在调用接口类型的方法时,如果编译期间不能确认接口的类型,Go 语言会在运行期间决定具体调用该方法的哪个实现

func main() {
    var c Duck = &Cat{Name: "grooming"}
    // 以 Duck 接口类型的身份调用,调用时需要经过运行时的动态派发
    c.Quack()
    // 以 *Cat 具体类型的身份调用,编译期就会确定调用的函数
    c.(*Cat).Quack()
}