了解struct embedding

ren*_*xis 1 methods struct go

有人可以解释一下为什么这段代码打印1而不是2?

package main

import (
    "fmt"
)

type S1 struct{
    f1 string
}

type S2 struct{
    S1
    f2 string
}   

func (s *S1) Say(){
    fmt.Println("1")
}   

func (s *S2) Say(){
    fmt.Println("2")
}       

type S3 S2

func main() {
    var s3 S3
    s3.Say()
}
Run Code Online (Sandbox Code Playgroud)

(Runnable at:https://play.golang.org/p/_cjNxBKgSf)

agh*_*ast 6

看到这个答案.

具体来说,从Go规范我们有方法集:

方法集

类型可以具有与其关联的方法集.接口类型的方法集是其接口.任何其他类型T的方法集由用接收器类型T声明的所有方法组成.相应指针类型*T的方法集是用receiver*T或T声明的所有方法的集合(也就是说,它还包含方法一套T).其他规则适用于包含嵌入字段的结构,如结构类型一节中所述.任何其他类型都有一个空方法集.在方法集中,每个方法必须具有唯一的非空方法名称.

然后结构类型:

结构类型

结构是一系列命名元素,称为字段,每个元素都有一个名称和一个类型.字段名称可以显式指定(IdentifierList)或隐式指定(EmbeddedField).在结构中,非空字段名称必须是唯一的.

然后这个:

使用类型但没有显式字段名称声明的字段称为嵌入字段.

最后,这个:

如果是表示该字段或方法的合法选择器,则结构x中的嵌入字段的字段或方法f被称为 提升.x.ff

提升字段的作用类似于结构的普通字段,除了它们不能用作结构的复合文字中的字段名称.

给定结构类型S和名为T的类型,提升的方法包含在结构的方法集中,如下所示:

If S contains an embedded field T, the method sets of S and *S 
both include promoted methods with receiver T. The method set of *S
also includes promoted methods with receiver *T.

If S contains an embedded field *T, the method sets of S and *S 
both include promoted methods with receiver T or *T.
Run Code Online (Sandbox Code Playgroud)

这一切如何结合?

你有

type S2 struct{
    S1
    f2 string
}
Run Code Online (Sandbox Code Playgroud)

这使得S1成为一个嵌入式领域,并使S1.Say可见.

然后你有:

type S3 S2
Run Code Online (Sandbox Code Playgroud)

这使得S3具有与S2相同的内存布局和字段,但不会创建类型等价.这是不是说S3"是" S2,而是S3 是不一样的S2,但他们确实有相同的布局.

该布局包括嵌入字段,恰好将S1.Say引入等式.

换句话说,类型S2有一个基础类型:

struct { S1; f2 string }
Run Code Online (Sandbox Code Playgroud)

和一个名为Say的方法.

类型S3具有相同的基础类型:

struct { S1; f2 string }
Run Code Online (Sandbox Code Playgroud)

但是S3和S2不一样,所以S3不会"继承"来自S2的任何方法.相反,S3仅从其基础类型继承字段/方法,即f2和S1.*(包括"Say").