一尘不染

类型断言后如何使用指针接收器调用方法?

go

我正在学习接口,类型转换和带有指针接收器的方法。指针接收器方法背后的规则和术语使我感到困惑。让我展示我对一个程序的困惑。

这是我的Go程序。

package main

import "fmt"

type Employee struct {
    Name string
}

func (e Employee) Hi() {
    fmt.Printf("Hi! I am %s.\n", e.Name)
}

func (e *Employee) Hello() {
    fmt.Printf("Hello! I am %s.\n", e.Name)
}

func main() {
    var a Employee = Employee{"Alice"}
    a.Hi()
    a.Hello()

    var b interface{} = Employee{"Bob"}
    b.(Employee).Hi()
    // b.(Employee).Hello()
}

这是输出。

Hi! I am Alice.
Hello! I am Alice.
Hi! I am Bob.

如果删除最后注释的行,则会出现此错误。

# command-line-arguments
./foo.go:24: cannot call pointer method on b.(Employee)
./foo.go:24: cannot take the address of b.(Employee)

如何修复该行代码,以便能够使用指针接收器调用该方法?请通过提出带有指针接收器的方法的概念来说明解决方案,以阐明为什么它不起作用。


阅读 230

收藏
2020-07-02

共1个答案

一尘不染

您不能(在这种情况下,对于指针接收器是隐式的)获取表达式(b.(Employee))的地址。您可以获取变量的地址。例如,

package main

import "fmt"

type Employee struct {
    Name string
}

func (e Employee) Hi() {
    fmt.Printf("Hi! I am %s.\n", e.Name)
}

func (e *Employee) Hello() {
    fmt.Printf("Hello! I am %s.\n", e.Name)
}

func main() {
    var a Employee = Employee{"Alice"}
    a.Hi()
    a.Hello()

    var b interface{} = Employee{"Bob"}
    b.(Employee).Hi()
    // b.(Employee).Hello()
    // main.go:24: cannot call pointer method on b.(Employee)
    // main.go:24: cannot take the address of b.(Employee)
    e := b.(Employee)  // e, a variable, is addressable
    e.Hello()

    var c interface{} = &Employee{"Chris"}
    c.(*Employee).Hi()
    c.(*Employee).Hello()
}

输出:

Hi! I am Alice.
Hello! I am Alice.
Hi! I am Bob.
Hello! I am Bob.
Hi! I am Chris.
Hello! I am Chris.

Go编程语言规范

类型断言

对于具有接口类型和类型T的表达式x,主要表达式

x.(T)

断言x不为nil,并且x中存储的值的类型为T。符号x。(T)称为类型断言。

如果类型断言成立,则表达式的值为存储在x中的值,其类型为T。如果类型断言为false,则会发生运行时恐慌。

来电

如果x的方法集包含m并且参数列表可以分配给m的参数列表,则方法调用xm()是有效的。如果x是可寻址的并且&x的方法集包含m,则xm()是(&x).m()的简写。

地址运算符

对于类型T的操作数x,地址操作&x生成指向 T的类型
T的指针。操作数必须是可寻址的,即变量,指针间接寻址或切片索引操作;或可寻址结构操作数的字段选择器;或可寻址数组的数组索引操作。除可寻址性要求外,x还可为(可能带有括号的)复合文字。

类型断言的值b.(Employee)是类型Employee。该方法调用b.(Employee).Hello()是速记的,(&b.(Employee)).Hello()因为它func (e *Employee) Hello()有一个指针接收器。但是,b.(Employee)表达式不能寻址。因此,

error: cannot call pointer method on b.(Employee)
error: cannot take the address of b.(Employee)
2020-07-02