Sam*_*ats 3 methods struct embedding 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)
}
Run Code Online (Sandbox Code Playgroud)
这段代码的输出是{name: John Doe, age: 35 3 Construction}
。但是如果我删除Person.String()
方法定义,那么输出就是3
(它调用engineer.TaxPayer.String()
)。但是,如果我也删除TaxPayer.String()
方法定义,则输出为{{John Doe 35} {3} Construction}
. 我最初认为必须String()
为整个Engineer
结构定义一个隐式方法,但没有这样的方法。
为什么方法调用会这样?如果我改为将每个嵌入类型的方法命名为String()
(例如Foo()
)以外的任何名称,然后尝试执行fmt.Println(engineer.Foo())
,我会收到(预期的)编译错误:ambiguous selector engineer.Foo
。当方法的名称String()
改为时,为什么不引发此错误?
如果在结构中嵌入类型,嵌入类型的字段和方法将提升为嵌入器类型。它们的“行为”就好像它们是在嵌入器类型上定义的一样。
这是什么意思?如果 typeA
嵌入 type B
,并且 typeB
有方法String()
,则可以调用String()
type A
(接收者仍然是B
,这不是继承也不是虚方法)。
到现在为止还挺好。但是如果 typeA
嵌入 typeB
和 type C
,两者都有一个String()
方法呢?那么A.String()
将是模棱两可的,因此在这种情况下String()
不会提升该方法。
这解释了你的经历。打印Engineer
将具有默认格式(结构字段),因为将有 2String()
种方法,因此没有用于Engineer
自身。当然,默认格式涉及打印字段,并且为了生成值的默认 string
表示,fmt
包检查正在打印的值是否实现了fmt.Stringer
,如果是,String()
则调用它的方法。
如果您删除Person.String()
,则只有一个String()
方法被提升, from TaxPlayer
,以便由fmt
包调用以生成值本身的string
表示Engineer
。
如果您删除TaxPayer.String()
: thenPerson.String()
将是唯一String()
提升的方法,因此它用于Engineer
值本身。
如果是表示该字段或方法的合法选择器,则结构中嵌入字段的字段或方法 称为提升。
f
x
x.f
f
[...] 给定一个 struct type
S
和一个定义的 typeT
,提升的方法包含在结构的方法集中,如下所示:
- 如果
S
包含嵌入字段T
,该方法集的S
和*S
两者都包括推进与接收器的方法T
。方法集*S
还包括带有接收器的提升方法*T
。- 如果
S
包含嵌入字段*T
,则S
和的方法集*S
都包含带有接收者T
或的提升方法*T
。
第一句陈述了“ifx.f
是一个合法的选择器”。法律是什么意思?
Run Code Online (Sandbox Code Playgroud)x.f
表示场或方法
f
的价值x
。[...] 选择器
f
可以表示一个f
类型的字段或方法T
,或者它可以引用f
嵌套的嵌入字段的字段或方法T
。走过来达到嵌入式领域的数f
称为它的深度在T
。中f
声明的字段或方法的深度T
为零。在嵌入字段A
中声明的字段或方法 fT
的深度是f
in的深度A
加一。以下规则适用于选择器:
- 对于
x
typeT
或*T
whereT
不是指针或接口类型的值,x.f
表示最浅深度的字段或方法,T
其中存在此类f
。如果不完全是f
最浅深度的,则选择器表达式是非法的。- [...]
强调了本质,它解释了为什么首先没有String()
调用任何方法:Engineer.String()
可能来自 2 个“来源”:Person.String
并且TaxPayer.String
,因此Engineer.String
是非法选择器,因此没有任何String()
方法将成为 的方法集的一部分Engineer
。
使用非法选择器会引发编译时错误(例如“模棱两可的选择器engineer.Foo”)。所以你得到错误是因为你明确地试图引用engineer.Foo
. 但只是嵌入两种类型都具有String()
,这不是编译时错误。嵌入本身不是错误。使用非法选择器将是错误的。如果你写engineer.String()
,那会再次引发编译时错误。但是如果你只是通过engineer
printing: fmt.Println(engineer)
,这里没有非法选择器,你不引用engineer.String()
. 这是允许的。(当然,由于方法 set ofEngineer
没有提升的String()
方法,因此Engineer
在打印字段时不会调用它来为–only生成字符串表示。)
归档时间: |
|
查看次数: |
67 次 |
最近记录: |