类型嵌入
在一个类型的定义中嵌入其它类型
接口
语义:方法集合并入
接口类型声明了由一个方法集合
代表的接口
1 2 3 4 5 6 7 8 9 10
| type E interface { M1() M2() }
type I interface { M1() M2() M3() }
|
完全等价
:类型嵌入,新接口类型将嵌入的接口类型的方法集合,并入
到自身的方法集合
中 - 接口组合
1 2 3 4 5 6 7 8 9
| type E interface { M1() M2() }
type I interface { E M3() }
|
Go 标准库
io/io.go1 2 3 4 5 6 7 8 9 10 11
| type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
type Closer interface { Close() error }
|
io/io.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| type ReadWriter interface { Reader Writer }
type ReadCloser interface { Reader Closer }
type WriteCloser interface { Writer Closer }
type ReadWriteCloser interface { Reader Writer Closer }
|
在 Go 1.14
之前:接口类型嵌入的接口类型的方法集合不能有交集
,且嵌入的方法名字不能与新接口的其它方法重名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package main
type I1 interface { M1() }
type I2 interface { M1() M2() }
type I3 interface { I1 I2 }
type I4 interface { I2 M2() }
func main() { }
|
从 Go 1.14
开始,移除了上述约束
接口类型只能嵌入接口类型
结构体
Embedded Field
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| type T1 int type t2 struct { n int m int }
type I interface { M1() }
type S1 struct { T1 *t2 I
a int b string }
|
用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| type MyInt int
func (n *MyInt) Add(m int) { *n += MyInt(m) }
type t struct { a int b int }
type S struct { *MyInt t io.Reader
s string n int }
func main() { m := MyInt(17) r := strings.NewReader("hello, go") s := S{ MyInt: &m, t: t{a: 1, b: 2}, Reader: r,
s: "demo", }
sl := make([]byte, len("hello, go")) _, _ = s.Reader.Read(sl) fmt.Println(string(sl))
s.MyInt.Add(5) fmt.Println(*s.MyInt) }
|
约束
嵌入字段类型的底层类型
不能为指针
类型
1 2 3 4 5
| type MyInt *int
type S struct { MyInt }
|
嵌入字段的名字
在结构体定义必须唯一
,即同一个结构体中无法容纳名字相同
的嵌入字段
1 2 3 4
| package a
type T struct { }
|
1 2 3 4
| package b
type T struct { }
|
1 2 3 4 5 6
| package main
type S struct { a.T b.T }
|
继承
实际为组合
(代理)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| func main() { m := MyInt(17) r := strings.NewReader("hello, go") s := S{ MyInt: &m, t: t{a: 1, b: 2}, Reader: r,
s: "demo", }
sl := make([]byte, len("hello, go")) _, _ = s.Read(sl) fmt.Println(string(sl))
s.Add(5) fmt.Println(*s.MyInt) }
|
- 通过结构体类型 S 的变量 s 调用 Read 方法
- Go 发现结构体类型 S 自身并没有定义 Read 方法
- Go 会查看 S 的
嵌入字段
对应的类型是否定义了 Read 方法
s.Read
会被转换
为 s.Reader.Read
- 嵌入字段 Reader 的 Read 方法被
提升
为 S 的方法,放入到了 S 的方法集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| func main() { m := MyInt(17) r := strings.NewReader("hello, go") s := S{ MyInt: &m, t: t{a: 1, b: 2}, Reader: r,
s: "demo", }
sl := make([]byte, len("hello, go")) _, _ = s.Read(sl) fmt.Println(string(sl))
s.Add(5) fmt.Println(*s.MyInt)
printMethods(s) }
func printMethods(i interface{}) { fmt.Println("== Methods ==") dump := reflect.TypeOf(i) for i := 0; i < dump.NumMethod(); i++ { fmt.Printf("%s\n", dump.Method(i).Name) } }
|
实际为组合
(delegate
),S 只是一个代理
方法集合
结构体类型可以嵌入任意自定义类型
或者接口类型
接口
嵌入的接口类型的方法集合并入
结构体类型 T
的方法集合(*T
类型的方法集合包含 T
类型的方法集合)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| type I interface { M1() M2() }
type T struct { I }
func (T) M3() {}
func dumpMethodSet(i interface{}) { fmt.Printf("== Methods of %T ==\n", i) dump := reflect.TypeOf(i) for i := 0; i < dump.NumMethod(); i++ { fmt.Printf("%s\n", dump.Method(i).Name) } }
func main() { var t T var p *T
dumpMethodSet(t) dumpMethodSet(p) }
|
当结构体
嵌入多个接口类型
的方法集合存在交集
时,可能
会编译报错
PS:接口类型嵌入接口类型,如果存在交集,从 Go 1.14 开始,不再编译报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| type E1 interface { M1() M2() M3() }
type E2 interface { M1() M2() M4() }
type T struct { E1 E2 }
func main() { t := T{}
t.M4() }
|
- 嵌入其它类型的结构体类型本身是一个
代理
- 在调用实例所代理的方法时,Go 会首先查看结构体
自身
是否实现了该方法
- 如果实现了,Go 会
优先
使用结构体自身实现的方法
- 如果没有实现,会查找结构体中
嵌入字段
的方法集合中,是否包含了该方法
- 如果多个嵌入字段都包含了
同名方法
,即方法集合存在交集
,此时编译器
将无法确定
使用哪个方法
- 解决方案
- 消除接口类型的方法集合存在
交集
的情况
- 在结构体类型中新增
自身
的实现,让编译器优先选择结构体的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| type E1 interface { M1() M2() M3() }
type E2 interface { M1() M2() M4() }
type T struct { E1 E2 }
func (T) M1() { fmt.Println("T.M1") } func (T) M2() { fmt.Println("T.M2") }
func main() { t := T{} t.M1() t.M2() }
|
简化单元测试
的编写:结构体类型也是其内部嵌入的接口类型的一个实现
employee.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package employee
type Result struct { Count int }
func (r Result) Int() int { return r.Count }
type Rows []struct{}
type Stmt interface { Close() error NumInput() int Exec(stmt string, args ...string) (Result, error) Query(args []string) (Rows, error) }
func MaleCount(s Stmt) (int, error) { result, err := s.Exec("select count(*) from employee_tab where gender=?", "1") if err != nil { return 0, err }
return result.Count, nil }
|
employee_test.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package employee
import "testing"
type fakeStmtForMaleCount struct { Stmt }
func (fakeStmtForMaleCount) Exec(stmt string, args ...string) (Result, error) { return Result{Count: 5}, nil }
func TestEmployeeMaleCount(t *testing.T) { f := fakeStmtForMaleCount{} count, _ := MaleCount(f) if count != 5 { t.Errorf("MaleCount(f) = %d; want 5", count) return } }
|
结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| type T1 struct{}
func (T1) T1M1() {} func (*T1) PT1M2() {}
type T2 struct{}
func (T2) T2M1() {} func (*T2) PT2M2() {}
type T struct { T1 *T2 }
func dumpMethodSet(i interface{}) { fmt.Printf("== Methods of %T ==\n", i) dump := reflect.TypeOf(i) for i := 0; i < dump.NumMethod(); i++ { fmt.Printf("%s\n", dump.Method(i).Name) } fmt.Println() }
func main() { t := T{ T1: T1{}, T2: &T2{}, }
dumpMethodSet(t.T1) dumpMethodSet(&t.T1)
dumpMethodSet(*t.T2) dumpMethodSet(t.T2)
dumpMethodSet(t) dumpMethodSet(&t) }
|
- 结构体类型的方法集合
包含
嵌入的接口类型
的方法集合
- 当结构体类型 T 包含嵌入字段 E 时,
*T
的方法集合要包含的是 *E
的方法集合(范围更广
)
类型定义
defined
通过类型声明
语法声明的类型都被称为 defined
类型,新定义的 defined 类型与原 defined 类型为不同类型
1 2 3 4 5 6 7 8 9
| type I interface { M1() M2() }
type T int
type NI I type NT T
|
基于接口类型
创建的 defined 接口类型的方法集合与原接口类型的方法集合完全一致
无隐式继承
:基于自定义非接口类型
的 defined 类型的方法集合为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| type T struct{}
func (t T) M1() {} func (t *T) M2() {}
type T1 T
func dumpMethodSet(i interface{}) { fmt.Printf("== Methods of %T ==\n", i) dump := reflect.TypeOf(i) for i := 0; i < dump.NumMethod(); i++ { fmt.Printf("%s\n", dump.Method(i).Name) } }
func main() { var t T var pt *T var t1 T1 var pt1 *T1
dumpMethodSet(t) dumpMethodSet(t1)
dumpMethodSet(pt) dumpMethodSet(pt1) }
|
alias
无论原类型是接口类型还是非接口类型,类型别名与原类型拥有完全相同
的方法集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| type T struct{}
func (t T) M1() {} func (t *T) M2() {}
type T1 = T
func dumpMethodSet(i interface{}) { fmt.Printf("== Methods of %T ==\n", i) dump := reflect.TypeOf(i) for i := 0; i < dump.NumMethod(); i++ { fmt.Printf("%s\n", dump.Method(i).Name) } }
func main() { var t T var pt *T var t1 T1 var pt1 *T1
dumpMethodSet(t) dumpMethodSet(t1)
dumpMethodSet(pt) dumpMethodSet(pt1) }
|
小结
是否继承
|
defined |
alias |
接口类型 |
Y |
Y |
非接口类型 |
N |
Y |
- 基于
非接口类型
的 defined
类型创建的新 defined 类型不会继承
原类型的方法集合 - 符合语义
- 通过
alias
定义的新类型和原类型拥有相同
的方法集合