如何在类型断言后调用具有指针接收器的方法?

huangapple go评论89阅读模式
英文:

How to invoke a method with pointer receiver after type assertion?

问题

我正在学习接口、类型转换和带指针接收器的方法。
指针接收器方法的规则和术语对我来说很困惑。
让我用一个程序来展示我的困惑。

这是我的 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)

如何修复这行代码,以便能够调用带有指针接收器的方法?请解释一种解决方案,并对为什么这样做不起作用的概念进行一些澄清。

英文:

I am learning interface, type conversions and methods with pointer receivers.
The rules and terminology behind pointer receiver methods are confusing to me.
Let me demonstrate my confusion with one program.

This is my Go program.

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

This is the output.

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

If I remove the last commented out line, I get this error.

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

How can I fix that line of code so that I am able to invoke the method with
pointer receiver? Please explain a solution with some clarification on why this
does not work by laying down the concepts of methods with pointer receiver.

答案1

得分: 7

在这种情况下(隐式地针对指针接收器),你不能获取表达式(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, 一个变量,是可寻址的
    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.

在这里,类型断言 b.(Employee) 的值是类型 Employee。方法调用 b.(Employee).Hello()(&b.(Employee)).Hello() 的简写,因为 func (e *Employee) Hello() 有一个指针接收器。但是,表达式 b.(Employee) 是不可寻址的。因此,

错误:无法在 b.(Employee) 上调用指针方法
错误:无法获取 b.(Employee) 的地址
英文:

You can't (in this case implicitly for a pointer receiver) take the address of the result of an expression (b.(Employee)). You can take the address of a variable. For example,

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

Output:

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

> The Go Programming Language Specification
>
> Type assertions
>
> For an expression x of interface type and a type T, the primary
> expression
>
> x.(T)
>
> asserts that x is not nil and that the value stored in x is of type T.
> The notation x.(T) is called a type assertion.
>
> If the type assertion holds, the value of the expression is the value
> stored in x and its type is T. If the type assertion is false, a
> run-time panic occurs.
>
> Calls
>
> A method call x.m() is valid if the method set of (the type of) x
> contains m and the argument list can be assigned to the parameter list
> of m. If x is addressable and &x's method set contains m, x.m() is
> shorthand for (&x).m()
>
> Address operators
>
> For an operand x of type T, the address operation &x generates a
> pointer of type *T to x. The operand must be addressable, that is,
> either a variable, pointer indirection, or slice indexing operation;
> or a field selector of an addressable struct operand; or an array
> indexing operation of an addressable array. As an exception to the
> addressability requirement, x may also be a (possibly parenthesized)
> composite literal.

The value of the type assertion b.(Employee) is of type Employee. The method call b.(Employee).Hello() is shorthand for (&b.(Employee)).Hello() since func (e *Employee) Hello() has a pointer receiver. But, b.(Employee), an expression, is not addressable. Therefore,

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

答案2

得分: -1

修复方法如下:

var b interface{} = &Employee{"Bob"}
b.(*Employee).Hello()

这里的b是一个实际上是"指向Employee的指针"的接口,然后进行了类型断言。需要记住的是,"Employee"和"指向Employee的指针"是两种完全不同的类型。

英文:

The fix would be:

var b interface{} = &Employee{"Bob"}
b.(*Employee).Hello()

Here b is an interface that is actually a "pointer to Employee" and then same has been type asserted. The thing to remember is that "Employee" and "pointer to Employee" are two different types altogether.

huangapple
  • 本文由 发表于 2017年5月10日 12:19:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/43883502.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定