From the article structs in Go, we know that a struct type can have many fields. Each field is composed of one field name and one field type. In fact, sometimes, a struct field can be composed of a field type only. The way to declare struct fields is called type embedding.
This article will explain the purpose of type embedding and all kinds of details in type embedding.
package main
import "net/http"
func main() {
type P = *bool
type M = map[int]int
var x struct {
string // a named non-pointer type
error // a named interface type
*int // an unnamed pointer type
P // an alias of an unnamed pointer type
M // an alias of an unnamed type
http.Header // a named map type
}
x.string = "Go"
x.error = nil
x.int = new(int)
x.P = new(bool)
x.M = make(M)
x.Header = http.Header{}
}
In the above example, six types are embedded in the struct type. Each type embedding forms an embedded field.
Embedded fields are also called as anonymous fields.
However, each embedded field has a name specified implicitly.
The unqualified type name
of an embedded field acts as the name of the field.
For example, the names of the six embedded fields in the above examples are
string
, error
, int
, P
, M
,
and Header
, respectively.
An embedded field must be specified as a type nameT
or as a pointer to a non-interface type name*T
, andT
itself may not be a pointer type.
The above description is accurate before Go 1.9.
However, with the introduction of type aliases in Go 1.9,
the description becomes a little outdated and inaccurate.
For example, the description doesn't include the case of
the P
field in the example in the last section.
T
can be embedded as an embedded field
unless T
denotes a named pointer type or a pointer type
whose base type is either a pointer or an interface type.
*T
, where T
is a type name denoting
the base type of the pointer type, can be embedded as an embedded field
unless type name T
denotes a pointer or interface type.
type Encoder interface {Encode([]byte) []byte}
type Person struct {name string; age int}
type Alias = struct {name string; age int}
type AliasPtr = *struct {name string; age int}
type IntPtr *int
type AliasPP = *IntPtr
// These types and aliases can be embedded.
Encoder
Person
*Person
Alias
*Alias
AliasPtr
int
*int
// These types and aliases can't be embedded.
AliasPP // base type is a pointer type
*Encoder // base type is an interface type
*AliasPtr // base type is a pointer type
IntPtr // named pointer type
*IntPtr // base type is a pointer type
*chan int // base type is an unmaed type
struct {age int} // unnamed non-pointer type
map[string]int // unnamed non-pointer type
[]int64 // unnamed non-pointer type
func() // unnamed non-pointer type
No two fields are allowed to have the same name in a struct,
there are no exceptions for anonymous struct fields.
By the embedded field naming rules,
an unnamed pointer type can't be embedded along with its base type
in the same struct type.
For example, int
and *int
can't be embedded in the same struct type.
A struct type can't embed itself or its aliases, recursively.
Generally, it is only meaningful to embed types who have fields or methods (the following sections will explain why), though some types without any field and method can also be embedded.
The main purpose of type embedding is to extend the functionalities of the embedded types into the embedding type, so that we don't need to re-implement the functionalities of the embedded types for the embedding type.
Many other object-oriented programming languages use inheritance to achieve the same goal of type embedding. Both mechanisms have their own benefits and drawbacks. Here, this article will not discuss which one is better. We should just know Go chose the type embedding mechanism, and there is a big difference between the two:T
inherits another type,
then type T
obtains the abilities of the other type.
At the same time, each value of type T
can also be
viewed as a value of the other type.
T
embeds another type,
then type other type becomes a part of type T
,
and type T
obtains the abilities of the other type,
but none values of type T
can be viewed as values of the other type.
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) PrintName() {
fmt.Println("Name:", p.Name)
}
func (p *Person) SetAge(age int) {
p.Age = age
}
type Singer struct {
Person // extends Person by embedding it
works []string
}
func main() {
var gaga = Singer{Person: Person{"Gaga", 30}}
gaga.PrintName() // Name: Gaga
gaga.Name = "Lady Gaga"
(&gaga).SetAge(31)
(&gaga).PrintName() // Name: Lady Gaga
fmt.Println(gaga.Age) // 31
}
From the above example, it looks that, after embedding type Person
,
the type Singer
obtains all methods and fields of type Person
,
and type *Singer
obtains all methods of type *Person
.
Are the conclusions right? The following sections will answer this question.
Singer
value is not a Person
value,
the following code doesn't compile:
var gaga = Singer{}
var _ Person = gaga
Singer
and the methods of type *Singer
used in the last example by using the reflection
functionalities provided in the reflect
standard package.
package main
import (
"fmt"
"reflect"
)
... // the types declared in the last example
func main() {
t := reflect.TypeOf(Singer{}) // the Singer type
fmt.Println(t, "has", t.NumField(), "fields:")
for i := 0; i < t.NumField(); i++ {
fmt.Print(" field#", i, ": ", t.Field(i).Name, "\n")
}
fmt.Println(t, "has", t.NumMethod(), "methods:")
for i := 0; i < t.NumMethod(); i++ {
fmt.Print(" method#", i, ": ", t.Method(i).Name, "\n")
}
pt := reflect.TypeOf(&Singer{}) // the *Singer type
fmt.Println(pt, "has", pt.NumMethod(), "methods:")
for i := 0; i < pt.NumMethod(); i++ {
fmt.Print(" method#", i, ": ", pt.Method(i).Name, "\n")
}
}
The result:
main.Singer has 2 fields:
field#0: Person
field#1: works
main.Singer has 1 methods:
method#0: PrintName
*main.Singer has 2 methods:
method#0: PrintName
method#1: SetAge
From the result, we know that the type Singer
really owns
a PrintName
method, and the type *Singer
really
owns two methods, PrintName
and SetAge
.
But the type Singer
doesn't own a Name
field.
Then why is the selector expression gaga.Name
legal for a Singer
value gaga
?
Please read the next section to get the reason.
From the articles structs in Go and
methods in Go, we have learned that,
for a value x
, x.y
is called a selector,
where y
is either a field name or a method name.
If y
is a field name,
then x
must be a struct value or a struct pointer value.
A selector is an expression, which represents a value.
If the selector x.y
denotes a field, it may also
has its own fields (if x.y
is a struct value) and methods.
Such as x.y.z
,
where z
can also be either a field name or a method name.
In Go, (without considering selector colliding and shadowing explained in a later section), if a middle name in a selector corresponds to an embedded field, then that name can be omitted from the selector. This is why embedded fields are also called anonymous fields.
package main
type A struct {
FieldX int
}
func (a A) MethodA() {}
type B struct {
*A
}
type C struct {
B
}
func main() {
var c = &C{B: B{A: &A{FieldX: 5}}}
// The following 4 lines are equivalent.
_ = c.B.A.FieldX
_ = c.B.FieldX
_ = c.A.FieldX // A is a promoted field of C
_ = c.FieldX // FieldX is a promoted field
// The following 4 lines are equivalent.
c.B.A.MethodA()
c.B.MethodA()
c.A.MethodA()
c.MethodA() // MethodA is a promoted method of C
}
This is why the expression gaga.Name
is legal in the example in the last section.
For it is just the shorthand of gaga.Person.Name
.
Similarly, the selector gaga.PrintName
can be viewed as
a shorthand of gaga.Person.PrintName
.
But, it is also okay if we think it is not a shorthand.
After all, the type Singer
really has a PrintName
method, though the method is declared implicitly (please read the section after next for details).
For the similar reason, the selector (&gaga).PrintName
and (&gaga).SetAge
can also be viewed as, or not as,
shorthands of (&gaga.Person).PrintName
and
(&gaga.Person).SetAge
.
Name
is called a promoted field of type Singer
.
PrintName
is called a promoted method of type Singer
.
Note, we can also use the selector gaga.SetAge
, only if
gaga
is an addressable value of type Singer
.
It is just syntactical sugar of (&gaga).SetAge
.
Please read method calls for details.
In the above examples, c.B.A.FieldX
is called the full form
of selectors c.FieldX
, c.B.FieldX
and c.A.FieldX
.
Similarly, c.B.A.MethodA
is called the full form of selectors
c.MethodA
, c.B.MethodA
and c.A.MethodA
.
If every middle name in the full form of a selector corresponds to
an embedded field, then the number of middle names in the selector
is called the depth of the selector.
For example, the depth of the selector c.MethodA
used
in an above example is 2, for the full form of the selector is
c.B.A.MethodA
.
x
(we should always assume it is addressable, even if it is not),
it is possible that many of its full-form
selectors have the same last item y
and every middle name of these selectors represents an embedded field.
For such cases,
x.y
.
In other words, x.y
denotes the full-form selector with the shallowest depth.
Other full-form selectors are shadowed by the one with the shallowest depth.
x.y
.
We say those full-form selectors with the shallowest depth are colliding with each other.
If a method selector is shadowed by another method selector, and the two corresponding method signatures are identical, we say the first method is overridden by the other one.
For example, assumeA
, B
and C
are three defined types.
type A struct {
x string
}
func (A) y(int) bool {
return false
}
type B struct {
y bool
}
func (B) x(string) {}
type C struct {
B
}
The following code doesn't compile.
The reason is the depths of the selectors v1.A.x
and v1.B.x
are equal, so the two selectors collide with each other and
neither of them can be shortened to v1.x
.
The same situation is for the selectors v1.A.y
and v1.B.y
.
var v1 struct {
A
B
}
func f1() {
_ = v1.x // error: ambiguous selector v1.x
_ = v1.y // error: ambiguous selector v1.y
}
The following code compiles okay. The selector
v2.C.B.x
is shadowed by v2.A.x
, so the
selector v2.x
is a shortened form of v2.A.x
actually.
For the same reason, the selector v2.y
is a shortened form of
v2.A.y
, not of v2.C.B.y
.
var v2 struct {
A
C
}
func f2() {
fmt.Printf("%T \n", v2.x) // string
fmt.Printf("%T \n", v2.y) // func(int) bool
}
Colliding or shadowed selectors don't prevent their deeper selectors being promoted. For example, the
.M
and .z
selectors still get promoted in the following example.
package main
type x string
func (x) M() {}
type y struct {
z byte
}
type A struct {
x
}
func (A) y(int) bool {
return false
}
type B struct {
y
}
func (B) x(string) {}
func main() {
var v struct {
A
B
}
//_ = v.x // error: ambiguous selector v.x
//_ = v.y // error: ambiguous selector v.y
_ = v.M // ok. <=> v.A.x.M
_ = v.z // ok. <=> v.B.y.z
}
One detail which is unusual but should be noted is that two unexported methods (or fields) from two different packages are always viewed as two different identifiers, even if their names are identical. So they will not never collide with or shadow each other when their owner types are embedded in the same struct type. For example, a program comprising two packages as the following shows will compile and run okay. But if all the
m()
occurrences are replaced with M()
, then the program will fail to compile
for A.M
and B.M
collide with each other, so c.M
is not a valid selector.
package foo // import "x.y/foo"
import "fmt"
type A struct {
n int
}
func (a A) m() {
fmt.Println("A", a.n)
}
type I interface {
m()
}
func Bar(i I) {
i.m()
}
package main
import "fmt"
import "x.y/foo"
type B struct {
n bool
}
func (b B) m() {
fmt.Println("B", b.n)
}
type C struct{
foo.A
B
}
func main() {
var c C
c.m() // B false
foo.Bar(c) // A 0
}
As mentioned above, both of type Singer
and
type *Singer
have a PrintName
method each,
and the type *Singer
also has a SetAge
method.
However, we never explicitly declare these methods
for the two types. Where do these methods come from?
S
embeds a type (or a type alias) T
and the embedding is legal,
T
,
if the selectors to that method neither collide with nor are shadowed
by other selectors, then compilers will implicitly declare
a corresponding method with the same specificaiton
for the embedding struct type S
.
And consequently, compilers will also
implicitly declare
a corresponding method for the pointer type *S
.
*T
,
if the selectors to that method neither collide with nor are shadowed
by other selectors, then compilers will implicitly declare
a corresponding method with the same specificaiton for the pointer type *S
.
struct{T}
and type *struct{T}
both obtain all the methods
of the type denoted by T
.type *struct{T}
.
*struct{T}
, type struct{*T}
,
and type *struct{*T}
all obtain all the methods of type *T
.
Singer
and type *Singer
.
// Note: these declarations are not legal Go syntax.
// They are shown here just for explanation purpose.
// They indicate how implicit method values are
// evaluated (see the next section for more).
func (s Singer) PrintName = s.Person.PrintName
func (s *Singer) PrintName = (*s).Person.PrintName
func (s *Singer) SetAge = (&(*s).Person).SetAge
The right parts are the corresponding full form selectors.
From the article methods in Go, we know that we can't explicitly declare methods for unnamed struct types and unnamed pointer types whose base types are unnamed struct types. But through type embedding, such unnamed types can also own methods.
If a struct type embeds a type which implements an interface type
(the embedded type may be the interface type itself),
then generally the struct type also implements the interface type,
exception there is a method specified by the interface type
shadowed by or colliding other methods or fields.
For example, in the above example program, both the embedding struct type and
the pointer type whose base type is the embedding struct type implement the interface type I
.
Age
has no methods, for it doesn't embed any types.
X
has two methods, IsOdd
and Double
.
IsOdd
is obtained by embedding the type MyInt
.
Y
has no methods, for its embedded the type Age
has not methods.
Z
has only one method, IsOdd
,
which is obtained by embedding the type MyInt
.
It doesn't obtain the method Double
from the type X
,
for it doesn't embed the type X
.
type MyInt int
func (mi MyInt) IsOdd() bool {
return mi%2 == 1
}
type Age MyInt
type X struct {
MyInt
}
func (x X) Double() MyInt {
return x.MyInt + x.MyInt
}
type Y struct {
Age
}
type Z X
Assume v.m
is a legal promoted method value expression,
compilers will normalize it as the result of changing implicit address taking
and pointer dereference operations into explicit ones in the corresponding
full form selector of v.m
.
The same as any other method value evaluation,
for a normalized method value expression v.m
,
at run time, when the method value v.m
is evaluated,
the receiver argument v
is evaluated and
a copy of the evaluation result is saved and used in later calls to the method value.
s.M1
is s.T.X.M1
. After changing the implicit address taking and
pointer dereference operations in it, it becomes (*s.T).X.M1
.
At run time, the receiver argument (*s.T).X
is evaluated
and a copy of the evaluation result is saved and used in later calls to the promoted method value.
The evaluation result is 1
,
that is why the call f()
always prints 1
.
s.M2
is s.T.X.M2
. After changing the implicit address taking and
pointer dereference operations in it, it becomes (&(*s.T).X).M2
.
At run time, the receiver argument &(*s.T).X
is evaluated
and a copy of the evaluation result is saved and used in later calls to the promoted method value.
The evaluation result is the address of the field s.X
(a.k.a. (*s.T).X
).
Any change of the value s.X
will be reflected through the dereference of the address,
but the changes of the value s.T
have no effects on the evaluation result,
that is why the two g()
calls both print 2
.
package main
import "fmt"
type X int
func (x X) M1() {
fmt.Println(x)
}
func (x *X) M2() {
fmt.Println(*x)
}
type T struct { X }
type S struct { *T }
func main() {
var t = &T{X: 1}
var s = S{T: t}
var f = s.M1 // <=> (*s.T).X.M1
var g = s.M2 // <=> (&(*s.T).X).M2
s.X = 2
f() // 1
g() // 2
s.T = &T{X: 3}
f() // 1
g() // 2
}
Not only can struct types embed other types, but also can interface types, but interface types can only embed interface types. Please read interfaces in Go for details.
package main
type I interface {
m()
}
type T struct {
I
}
func main() {
var t T
var i = &t
t.I = i
i.m() // will call t.m(), then call i.m() again, ...
}
The Go 101 프로젝트는 Github 에서 호스팅됩니다. 오타, 문법 오류, 부정확한 표현, 설명 결함, 코드 버그, 끊어진 링크와 같은 모든 종류의 실수에 대한 수정 사항을 제출하여 Go 101을 개선을 돕는 것은 언제나 환영합니다.
주기적으로 Go에 대한 깊이 있는 정보를 얻고 싶다면 Go 101의 공식 트위터 계정인 @go100and1을 팔로우하거나 Go 101 슬랙 채널에j가입해주세요.
reflect
표준 패키지sync
표준 패키지sync/atomic
표준 패키지