在 Go 中的指针方法中初始化指针接收器

12

如何使用指针方法初始化指针接收器?

package main

import "fmt"

type Person struct {
    name string
    age  int
}

func (p *Person) Born() {

    if nil == p {
        p = new(Person)
    }

}

func main() {

    var person *Person
    person.Born()
    if person == nil {
        fmt.Println("This person should be initialized. Why is that not the case?")
    }
    fmt.Println(person)
}

在调用指针接收器.Born()方法后,人们会期望该对象被初始化(清零)。但事实并非如此。是否有人能够解释一下这个问题?


var person *Person 没有意义。Person 是一个结构体。 - Gravy
2
@Gravy 当然有意义。它将 person 定义为一个未初始化的指向 Person 结构体的指针。只是在这种情况下,不是 OP 想要的。 - Jonathan Hall
1
person which has type pointer to Person is initialized with it's zero value - nil - Eugene Lisitsky
4个回答

18

在调用指针接收者的.Born()方法之后,人应该被初始化(清零)。

调用接收者的方法假定接收者已经被初始化。

因此,您需要进行初始化:

var person *Person
person = &Person{}  // Sets the pointer to point to an empty Person{} struct

或者用一句话来说:

var person = &Person{}

或者简写:

person := &Person{}

你打算进行的自我初始化失败的原因:

func (p *Person) Born() {
    if nil == p {
        p = new(Person)
    }
}

这意味着你新的任务是将p限定在Born()函数内部,因此在函数外部它没有任何效果。


非常感谢Flimzy的回复。 - Francis

2

显然,person不会被Born方法初始化。参数通过将实参赋值给形参来传递。

The Go Programming Language Specification

The type of a method is the type of a function with the receiver as first argument.

type Point struct{ x, y float64 }

func (p *Point) Scale(factor float64) {
  p.x *= factor
  p.y *= factor
}

For instance, the method Scale has type

func(p *Point, factor float64)

However, a function declared this way is not a method.

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the calling function when the function returns.

为了举例说明,这里有各种形式的Born方法调用。 p的范围仅限于方法或函数调用。
package main

import "fmt"

type Person struct {
    name string
    age  int
}

// Method
func (p *Person) Born() {
    if nil == p {
        p = new(Person)
    }
}

// Method as function
func Born(p *Person) {
    if nil == p {
        p = new(Person)
    }
}

func main() {

    // Initial method call form
    {
        var person *Person
        person.Born()
        fmt.Println(person)
    }

    // Equivalent method call by value form
    {
        var person *Person
        {
            p := person
            p.Born()
        }
        fmt.Println(person)
    }

    // Equivalent method call as function call form
    {
        var person *Person
        {
            p := person
            Born(p)
        }
        fmt.Println(person)
    }

    // Equivalent method call as inline function form
    {
        var person *Person
        {
            p := person
            if nil == p {
                p = new(Person)
            }
        }
        fmt.Println(person)
    }

}

输出:

<nil>
<nil>
<nil>
<nil>

1
我认为你需要的是“构造函数”或“工厂函数”:
type Person struct {
    name string
    age  int
}

func NewPerson(name string) *Person {
    return &Person{
        name: name,
    }
}

person := NewPerson("John Doe")

一般建议尝试以这样的方式定义类型,使它们的所谓“零值”——当未显式初始化变量时该类型变量获得的值——可以立即使用。在您的情况下,值得怀疑的是Person的零值是否合理,因为它将具有age为0的属性,这是完全合理的,而name为空字符串,这可能是可以接受的,也可能不是。

谢谢你,Kostix。我的问题纯粹是出于教育目的,我知道工厂模式是正确的选择。 - Francis

0

NewPerson 函数可以初始化一个人,也可以使用 Person 结构体和 Born 方法来获得一个新的人。

package main

import (
    "fmt"
    "time"
)

type Person struct {
    Name string
    Dob  time.Time
}

func NewPerson(name string, year, month, day int) *Person {
    return &Person{
        Name: name,
        Dob:  time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local),
    }
}

func (p *Person) GetAge() string {
    d := time.Since(p.Dob)
    return fmt.Sprintf("%s's age is %d", p.Name, int(d.Hours()/24/365))
}

func (p *Person) Born() {
    p.Name = "New born (unnamed)"
    p.Dob = time.Now()
}

func main() {
    joe := NewPerson("Joe", 1999, 12, 31)
    joeAge := joe.GetAge()
    fmt.Println(joeAge)

    newPerson := &Person{}
    newPerson.Born()
    newPersonAge := newPerson.GetAge()
    fmt.Println(newPersonAge)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接