String() 方法在 Go 中嵌入式类型的奇怪行为

我无法理解 String() 方法如何用于 Go 中的嵌入式结构。请考虑以下情况:


type Engineer struct {

    Person

    TaxPayer

    Specialization string

}


type Person struct {

    Name string

    Age  int

}


func (p Person) String() string {

    return fmt.Sprintf("name: %s, age: %d", p.Name, p.Age)

}


type TaxPayer struct {

    TaxBracket int

}


func (t TaxPayer) String() string {

    return fmt.Sprintf("%d", t.TaxBracket)

}


func main() {

    engineer := Engineer{

        Person: Person{

            Name: "John Doe",

            Age:  35,

        },

        TaxPayer: TaxPayer{3},

        Specialization: "Construction",

    }

    fmt.Println(engineer)

}

此代码的输出为 。但是,如果我删除方法定义,则输出只是(它调用)。但是,如果我也删除方法定义,则输出为 。我最初认为必须为整个结构定义一个隐式方法,但没有这样的方法。{name: John Doe, age: 35 3 Construction}Person.String()3engineer.TaxPayer.String()TaxPayer.String(){{John Doe 35} {3} Construction}String()Engineer


为什么方法调用会以这种方式运行?如果我将每个嵌入式类型的方法命名为除(比如说)以外的任何东西,然后尝试这样做,我得到一个(预期的)编译错误:。为什么在改为方法名称时不引发此错误?String()Foo()fmt.Println(engineer.Foo())ambiguous selector engineer.FooString()


千巷猫影
浏览 96回答 1
1回答

大话西游666

如果在结构中嵌入类型,则嵌入类型的字段和方法将提升为嵌入类型。它们“作用”就像在嵌入器类型上定义一样。这是什么意思?如果类型嵌入类型,并且类型有一个方法,你可以调用类型(接收方仍然是,这不是继承也不是虚方法)。ABBString()String()AB目前为止,一切都好。但是,如果类型嵌入了类型和类型,两者都有一个方法呢?然后会模棱两可,因此在这种情况下,该方法将不会被推广。ABCString()A.String()String()这解释了您的经历。打印将具有默认格式(结构字段),因为将有 2 种方法,因此没有一个用于其自身。当然,默认格式设置涉及打印字段,并且为了生成值的默认表示形式,包会检查正在打印的值是否实现 ,如果是,则调用其方法。EngineerString()Engineerstringfmtfmt.StringerString()如果删除 ,则只有一个方法被提升,from ,因此该方法由包调用以生成值本身的表示形式。Person.String()String()TaxPlayerfmtstringEngineer同样,如果你删除 :那么将是唯一提升的方法,所以它用于值本身。TaxPayer.String() Person.String()String()Engineer这在 Spec: 结构类型中有详细说明:如果结构中嵌入字段的字段或方法是表示该字段或方法的合法选择器,则称为升级。fxx.ff[...]给定一个结构类型和一个定义的类型,升级的方法包含在结构的方法集中,如下所示:ST如果包含一个嵌入字段,则 方法集 和 两者都包括带有接收器的提升方法。的方法集还包括带有接收器的提升方法。STS*ST*S*T如果包含一个嵌入字段,则 的方法集 和 两者都包括带有 receiver 或 的升级方法。S*TS*ST*T第一句说“如果x.f是法定选择器”。法律是什么意思?规格: 选择器:对于不是包名称的主表达式,选择器表达式xx.f表示值 的字段或方法。fx[...]选择器可以表示某种类型的字段或方法,也可以引用 嵌套嵌入字段 的字段或方法。遍历以达到的嵌入场的数量称为 其深度在 中。中声明的字段或方法的深度为零。在 嵌入的字段中声明的字段或方法 f 的深度是 in 加一的深度。ffTfTfTfTATfA以下规则适用于选择器:对于类型或 where 的值不是指针或接口类型,表示存在此类 .如果深度不正好有一个 f,则选择器表达式是非法的。xT*TTx.fTf[...]强调了本质,并解释了为什么首先没有调用任何方法:可能来自2个“源”:和,因此是非法的选择器,因此没有一个方法将是方法集的一部分。String()Engineer.String()Person.StringTaxPayer.StringEngineer.StringString()Engineer使用非法选择器会引发编译时错误(例如“不明确的选择器工程师。福”)。因此,您收到错误,因为您显式尝试引用 。但是仅仅嵌入2种类型都有,这不是编译时错误。嵌入本身不是错误。使用非法选择器将是错误。如果你写 ,那会再次引发编译时错误。但是如果你只是通过打印:,这里没有非法的选择器,你就不参考了。这是允许的。(当然,由于方法集 没有提升的方法,因此只有在打印字段时才会调用它来生成 – 的字符串表示形式。engineer.FooString()engineer.String()engineerfmt.Println(engineer)engineer.String()EngineerString()Engineer
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go