Golang 接口 & 指针

如 http.Handler 接口,有个困惑,为什么 第一个参数是value类型,第二个参数是指针类型呢?

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

Go里一切传递,都是传值,即便是指针。其实指针是一种特殊的类型包含了地址的值。

更进一步的发现,ResponseWriter 是接口类型。

type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(int)
}

interface 在运行时,会保存一个实现接口的对象,结构体里有两行,一个指向类型,另一个指向它关联的数据。

如果没有任何的数据绑定在接口上,Go可以指示接口实现,并提供编译时候的类型检查。

//(1) The interface
type Mutable interface {
    mutate(newValue string) error
}
//(2) Struct
type Data struct {
    name string
}
//(3) Implements the interface with a pointer receiver
func (d *Data) mutate(newValue string) error {
    d.name = newValue
    return nil
}
//(4) Function that accepts the interface
func mutator(mute Mutable) error {
    return mute.mutate("mutate")
}
func main() {
    d := Data{name: "fresh"}
    fmt.Println(d.name) //fresh
    //(5) pass as a pointer
    mutator(&d)
    fmt.Println(d.name) //mutate
}

在第三步,Data 实现了指针,可以改变内部的值

在第四步,函数 mutator 接受一个参数,来执行真正的改变操作。注意这里的参数是一个值,而不是指针。

第五步,我们传递了一个指针进去。貌似它违反了通常的golang 类型检查 type-check。例如,传递正常的类型会失败,但是传递接口是可以成功的。

例如下面的,就会编译失败

func thisWontWork(s string) { ... }
v := "kirk"
thisWontWork(&v) //compilation error

参数的类型检查,确保参数 interface 和 结构体的 receiver 一致。

           ---------------------------------------------
           | method receiver |  parameter              |
           | ------------------------------------------|
           |    pointer      |  pointer                |                       
           |-------------------------------------------|
           |    value        |  value                  |
           |                 |  pointer (dereferenced) |                  
           ---------------------------------------------

让测一下上述的假设,通过改变 receiver 为一个 值类型,并传参为值。

//(1) Implements the interface with a value receiver
func (d Data) mutate(newValue string) error {
...
//(2) mutator function is UNCHANGED
func mutator(mute Mutable) error {
//(3) pass as value
mutator(d)
fmt.Println(d.name) //fresh

你仍然可以看到,这个结构体,同样符合 mutator 函数的要求。但是该函数可能不太符合我们要求了,因为原始的值没有改变。

以下会编译错误:

//(1) Implements the interface with a pointer receiver
func (d *Data) mutate(newValue string) error {
//(2) Function is UNCHANGED
func mutator(mute Mutable) error {
//(3) Pass as value. Results in compilation error.
mutator(d)

interface 的实现者,可以有不同的类型的receivers

//(1) Define another struct that implements the interface
type AnotherData struct {...}
//(2) Implements as a value receiver.
func (ad AnotherData) mutate(newValue string) error {...}
//(3) Function is UNCHANGED
func mutator(mute Mutable) error {...}
//(4) Pass as a value.
anotherData := AnotherData{name: "afresh"}
fmt.Println(anotherData.name) //afresh
mutator(anotherData)
fmt.Println(anotherData.name) //afresh

回到最初我们讨论的 Handler, 这里的response 结构体实现了 ResponseWriter 接口,同样带着 指针类型的receivers
【这里要解释 receiver和函数的区别】
Http Server 生成了一个 返回结构体,然后作为一个指针传递给handlers,这样可以使得 handlers 写值进入response 对象。

//server.go - showing relevant code
package http
type response struct {
...
//methods of ResponseWriter implemented by response
func (w *response) Header() Header {...
func (w *response) WriteHeader(code int) {...
//readRequest returns a pointer to the response
func ... readRequest(..) (w *response, err error) {
   return &response{...}
}
//serves the request
func (c *conn) serve(ctx context.Context) {
  w_ptr, _ = readRequest(...)
  ...
  handler.ServeHTTP(w_ptr)

所以通常看来,即便 interface 可以被传值,更多的时候,它被作为指针来传递。

总结:

  1. 接口的实现方式决定了要传递的参数类型
  2. 值和指针receivers都是允许的,相同的接口,可以有不同的实现。
  3. 有人说 receivers 应该总是指针类型的,我会反驳,语言应该提供一个强制机制。

猜你喜欢

转载自blog.csdn.net/weixin_44328662/article/details/86619692