# 2.6 组合
go支持组合,即一种结构体包含另外一个结构体。在一些语言中,这叫混入类或者特性。语言总是不能实现简明的组合机制。在java中:
```java
public class Person {
private String name;
public String getName() {
return this.name;
}
}
public class Saiyan {
// 这表明`Saiyan`有一个`person`
private Person person;
// 可以使用`person`调用方法
public String getName() {
return this.person.getName();
}
...
}
```
这样语法太繁琐了。每个`Person`的方法在`Saiyan`中都被复写一遍。go避免这样繁琐的方式:
```go
type Person struct {
Name string
}
func (p *Person) Introduce() {
fmt.Printf("Hi, I'm %s\n", p.Name)
}
type Saiyan struct {
*Person
Power int
}
// 使用:
goku := &Saiyan{
Person: &Person{"Goku"},
Power: 9001,
}
goku.Introduce()
```
结构体`Saiyan`有一个字段时`*Persion`类型。因此我们没有明确的给它一个字段名,我们可以间接的使用这个组合类型的字段和方法。然而,go编译器给会给该字段一个名字,认为这是完全有效的。
```go
goku := &Saiyan{
Person: &Person{"Goku"},
}
fmt.Println(goku.Name)
fmt.Println(goku.Person.Name)
```
上面代码都将打印`Goku`。
组合优于继承吗?很多人都认为组合是一种更健壮的共享代码的方式。当你使用继承,你的类将和你的超类紧耦合,并且你最终更关注继承,而不是行为。
## 2.6.1 重载
虽然重载不是针对结构体,但是也值得提及。简单来说,go不支持重载。因此你会看见(和写)很多函数诸如`Load`、`LoadById`和 `LoadByName`等等。
然而,因为匿名组合只是一个编译技巧,我们能“重写”一个组合类型的方法。例如,我们的结构体`Saiyan`可以定义自己的`Introduce`方法:
```go
func (s *Saiyan) Introduce() {
fmt.Printf("Hi, I'm %s. Ya!\n", s.Name)
}
```
这种组合版本总是可以通过`s.Person.Introduce()`调用`Introduce()`方法。
## 链接
- 关于本书
- 引言
- 准备工作
- 安装开发环境
- 开始使用Go
- 创建一个Go模块
- 第1章:基础知识
- 1.1 编译
- 1.2 静态类型
- 1.3 类c语法
- 1.4 垃圾回收
- 1.5 运行go代码
- 1.6 导入包
- 1.7 变量和声明
- 1.8 函数声明
- 1.9 继续之前
- 第2章:语法学习
- 2.1 声明和初始化
- 2.2 结构体上的函数
- 2.3 构造函数
- 2.4 new
- 2.5 结构体字段
- 2.6 组合
- 2.7 指针类型和值类型
- 2.8 继续之前
- 第3章:复杂类型
- 3.1 数组
- 3.2 切片
- 3.3 映射
- 3.4 指针类型和值类型
- 3.5 继续之前
- 第4章:面向对象
- 4.1 包
- 4.2 接口
- 4.3 继续之前
- 第5章:综合知识
- 5.1 错误处理
- 5.2 defer
- 5.3 go语言风格
- 5.4 初始化的if
- 5.5 空接口和转换
- 5.6 字符串和字节数组
- 5.7 函数类型
- 5.8 内存分配
- 第6章:高并发
- 6.1 go协程
- 6.2 同步
- 6.3 通道
- 6.4 继续之前
- 第7章:工具库
- 7.1 类型转换
- 7.2 时间操作
- 第8章:程序测试
- 单元测试
- 性能测试
- 第9章:简单实例
- 内存分配
- 第10章:项目实战
- 结论
- 附录