返回

揭秘 Go 接口为何偏爱指针类型

后端

指针类型和值类型是 Go 中最基本的两种数据类型。值类型存储实际的值,而指针类型存储指向实际值的内存地址。当函数接收值类型时,它将创建该类型的副本,然后将副本传递给函数。这意味着函数只能对副本进行操作,而不能改变原始值。

当函数接收指针类型时,它将获得指向实际值的内存地址。这意味着函数可以直接访问和修改实际值。因此,指针类型常用于需要修改函数参数的情况。

接口是 Go 中的一种特殊类型,它允许不同类型的对象共享相同的行为。接口定义了一组方法,任何实现了这些方法的类型都可以实现该接口。当函数接收接口类型时,它并不关心具体实现该接口的类型是什么,它只关心该接口定义的方法。

但是,接口不能接收值类型,只能接收指针类型。这是因为值类型无法保证一定能够取得到地址。例如,如果一个值类型是匿名字段类型,那么它就没有地址。此外,如果一个值类型是逃逸的,那么它也有可能没有地址。

因此,为了保证接口能够接收所有实现了该接口的类型,接口只能接收指针类型。指针类型总是可以取到地址,因此接口可以放心接收指针类型。

在实际开发中,我们经常需要使用接口来定义函数的参数类型。例如,我们可以定义一个名为 Service 的接口,该接口定义了 Get()Set() 两个方法。然后,我们可以实现该接口,并使用该接口来定义函数的参数类型。

type Service interface {
    Get() string
    Set(value string)
}

type ServiceImpl struct {
    value string
}

func (s *ServiceImpl) Get() string {
    return s.value
}

func (s *ServiceImpl) Set(value string) {
    s.value = value
}

func main() {
    var s Service = &ServiceImpl{}
    s.Set("Hello, World!")
    fmt.Println(s.Get()) // 输出: Hello, World!
}

在这个示例中,我们定义了 Service 接口和 ServiceImpl 类型。ServiceImpl 类型实现了 Service 接口,并且我们在 main() 函数中使用了 Service 接口来定义函数的参数类型。

在上面的示例中,我们使用了指针类型 *ServiceImpl 来实现 Service 接口。这是因为 Service 接口只能接收指针类型。如果我们使用值类型 ServiceImpl 来实现 Service 接口,那么编译器会报错。

这就是 Go 接口为何偏爱指针类型的原因。指针类型总是可以取到地址,因此接口可以放心接收指针类型。