即使我使用指向类型的指针更新对象,我的对象也不会更新



我将一些Individual对象存储在一个切片中。在将其附加到切片之前,我打印Individual对象的名称。

将其存储在切片中后,我将其作为指针检索并想将名称更改为"Peter",但更改不起作用,因为它仍然打印"Steve"。为什么?

type Individual interface {
GetName() *string
SetName(name string)
}
type Person struct {
name string
}
// Implement functions of the Individual interface
func (p Person) GetName() *string  {
return &p.name
}
func (p Person) SetName(newName string)  {
name := p.GetName();
*name = newName
}

var individuals []Individual
func main() {
person := Person{name: "Steve"}
fmt.Println(person)
individuals = append(individuals, person) // append Person to slice
p1 := individuals[0]     // Retrieve the only Person from slice
p1.SetName("Peter")      // Change name
fmt.Println(p1)          // Should print "Peter", but prints "Steve"
fmt.Println(person)      // Should print "Peter", but prints "Steve"
}

每当方法想要修改接收器时,它必须是指向值的指针;该方法必须具有指针接收器。

如果方法具有非指针接收器,则在调用该方法时将创建并传递副本。从那时起,无论您使用接收器做什么,您都只能修改副本,而不能修改原始副本。

所以在你的例子中:

func (p Person) SetName(newName string)  {
name := p.GetName();
*name = newName
}

调用SetName()时,将创建Person的副本。在SetName()内部,您可以获取副本name字段的地址,您可以对其进行修改。(实际上,副本的副本,稍后会详细介绍...

解决方案:使用指针接收器:

func (p *Person) SetName(newName string)  {
name := p.GetName();
*name = newName
}

从这一点开始,只有*Person实现Individual,所以在追加时使用指针:

individuals = append(individuals, &person)

这很棘手,因为在此之后它将无法正常工作。为什么?

这是因为Person.GetName()仍然有一个非指针接收器:

func (p Person) GetName() *string {
return &p.name
}

所以当从SetName()调用GetName()时,会再次制作一个副本,GetName()会返回副本name字段的地址,SetName()只会修改为调用GetName()而创建的副本。

因此,要使所有工作,您还必须使用指针接收器进行GetName()

func (p *Person) GetName() *string {
return &p.name
}

现在它正在工作,输出(在Go Playground上尝试):

{Steve}
&{Peter}
{Peter}

但要知道,最简单和推荐的方法很简单:

func (p *Person) SetName(newName string) {
p.name = newName
}

仅此而已。

解决此问题的两件事:

  • 您需要通过"指针"方法附加方法,否则名称只会在方法内部更改。
  • 您需要对实际的Person变量使用指针,因为它们需要实现接口
type Individual interface {
GetName() *string
SetName(name string)
}
type Person struct {
name string
}
// Implement functions of the Individual interface
func (p Person) GetName() *string  {
return &p.name
}
func (p *Person) SetName(newName string)  {
p.name = newName
}

var individuals []Individual
func main() {
person := &Person{name: "Steve"}
fmt.Println(person)
individuals = append(individuals, person) // append Person to slice
p1 := individuals[0]     // Retrieve the only Person from slice
p1.SetName("Peter")      // Change name
fmt.Println(p1)          // Prints "Steve"
fmt.Println(person)      // Prints "Steve"
}

围棋游乐场的例子。

看到 https://play.golang.org/p/eg8oYDV6Xx p1 和 p2 已经是地址,你不需要它们的地址(地址的地址),只需打印 p1 和 p2 - 它们将与您看到的相同。

最新更新