使用值或指针接收器实现 Stringer 接口

Hie*_* Le 6 go

我尝试Stringer在我的类型上实现接口如下:

package main

import (
    "fmt"
)

type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.
func (o IPAddr) String() string {
    return fmt.Sprintf("%v.%v.%v.%v",  o[0], o[1], o[2], o[3])
}

func main() {
    hosts := map[string]IPAddr{
        "loopback":  {127, 0, 0, 1},
        "googleDNS": {8, 8, 8, 8},
    }
    for name, ip := range hosts {
        fmt.Printf("%v: %v\n", name, ip)
        fmt.Printf("%v\n",  ip.String())
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我使用了一个值接收器来实现该String()方法。在Printf承认我的实现,并呼吁正确的String我型功能。

输出:

googleDNS: 8.8.8.8
8.8.8.8
loopback: 127.0.0.1
127.0.0.1
Run Code Online (Sandbox Code Playgroud)

然后我更新了我的代码以使用指针接收器:

func (o *IPAddr) String() string {
    return fmt.Sprintf("%v.%v.%v.%v",  o[0], o[1], o[2], o[3])
}
Run Code Online (Sandbox Code Playgroud)

更新代码的输出:

loopback: [127 0 0 1]
127.0.0.1
googleDNS: [8 8 8 8]
8.8.8.8
Run Code Online (Sandbox Code Playgroud)

Printf方法不再调用我的String方法。输出告诉我Printf使用String该类型的默认方法。但是,当我调用 时ip.String(),使用了我的方法。

有人可以向我解释这种行为吗?据我所知,我们可以通过值和指针接收器来实现接口的方法。

谢谢你。

小智 6

%v转换指定将读取任何满足方法Stringer接口。但是,要发生这种情况,该方法必须存在于值的方法集中。

对于 type 值T,其方法集包含任何接收该类型值的方法T

func (t  T) Foo()    //     in the method set of T
func (t *T) Bar()    // not in the method set of T
Run Code Online (Sandbox Code Playgroud)

对于 type 指针*T,其方法集包含接收 type 值的方法和 typeT指针*T

func (t  T) Foo()    //     in the method set of *T
func (t *T) Bar()    //     in the method set of *T
Run Code Online (Sandbox Code Playgroud)

在 中main,您有一个标识为iptype的值IPAddr,因此上面的第一组注释代码适用。

您的第一个示例将起作用,因为该方法的方法接收器String具有 type IPAddr

在第二个示例中,方法的方法接收者String具有 type *IPAddr,这意味着它不在ip具有 type的方法集中IPAddr

总之:

            | String() Method | fmt.Print, fmt.Printf, etc.
 Input Type | Receiver        | calls String() implicitly
 ========== | =============== | ===========================
   *IPAddr  |     IPAddr      | Yes
            |    *IPAddr      | Yes
 ---------- + --------------- + ---------------------------
    IPAddr  |     IPAddr      | Yes
            |    *IPAddr      | No
Run Code Online (Sandbox Code Playgroud)

您可能想知道为什么会发生这种情况。事实证明,某些值可能无法寻址,因此具有类型的方法接收器*IPAddr无法接收没有地址的值。例如,尝试IPAddr{}.String()使用*IPAddr方法接收器执行。它将无法编译,因为文字值没有地址。如果你改为使用(&IPAddr{}).String(),它会起作用,因为现在你有一个*IPAddr使用创建的指针&IPAddr{},如果你使用非指针接收器IPAddr,那么无论 是否IPAddr可寻址,它都会起作用。