- 继承(结构体嵌入)
- 多态(接口实现和空接口)
1. 继承(结构体嵌入)
Go 语言没有传统的面向对象的继承机制,但可以通过“结构体嵌入”实现类似继承的效果。
- 结构体嵌入:在结构体中嵌入另一个结构体,使得子结构体可以直接访问父结构体的字段和方法。
- 字段重写:若子结构体定义了与嵌入的结构体同名的字段,则可以认为“重写”了父结构体的同名字段,访问时默认访问子结构体自己的字段,若需要访问父结构体的字段,则使用
StructName.FieldName
。
代码示例:
// 文件名: 03_inheritance.go
package main
import "fmt"
// 定义父类结构体 Person1
type Person1 struct {
name string
age int
}
// 定义另一个父类结构体 Person2(用于展示多个继承时的处理,字段冲突时需要显式区分)
// type Person2 struct {
// name string
// age int
// }
// 定义子类结构体 Teacher,通过嵌入 Person1 来实现继承
type Teacher struct {
Person1 // 嵌入 Person1,实现继承
// 如果想继承多个,则可以嵌入 Person2,但注意字段会冲突,需要通过显式调用来区分
// Person2 // 多继承示例
subject string
name int // 子类中重写了 name 字段(此字段会覆盖 Person1 中的同名字段)
}
// 为 Person1 定义一个方法
func (p *Person1) Hello() {
fmt.Println("I am Person1....")
}
func main() {
// 通过字面量实例化 Teacher,初始化 Person1 部分和子类特有字段
t2 := Teacher{
Person1: Person1{"zhangsan", 12},
subject: "math",
}
fmt.Printf("t2: %+v\n", t2)
// 先实例化一个 Person1 对象,然后将其赋值给 Teacher 的 Person1 字段
p1 := Person1{"mikodo", 19}
t3 := Teacher{Person1: p1, subject: "golang"}
fmt.Printf("t3: %+v\n", t3)
// 调用继承的方法
t2.Hello() // 等效于 t2.Person1.Hello()
// 演示字段重写
t2.name = 100 // 修改 Teacher 中的 name 字段(子类自己的字段)
t2.Person1.name = "luobozi" // 修改嵌入的 Person1 的 name 字段
fmt.Printf("t2 after change: %+v\n", t2)
}
说明:
- 结构体
Teacher
嵌入了Person1
,因此可以直接调用Person1
的方法,如Hello()
。 - 子类
Teacher
定义了自己的name
字段,这样在访问时默认访问的是Teacher.name
;如需要访问父结构体中的name
则使用Teacher.Person1.name
。
2. 多态
Go 语言多态主要通过接口(interface)实现。
- 接口定义:接口定义了一组方法,任何实现了这些方法的类型都被视为该接口类型。
- 鸭子类型:Go 的多态不关心具体类型,只关心是否具有接口所需的方法。
- 空接口:空接口
interface{}
可以表示任意类型,相当于其他语言的 Object 类型。
代码示例:
// 文件名: 03_polymorphism.go
package main
import "fmt"
// 定义 MoneyPay 接口,要求实现 pay 方法
type MoneyPay interface {
pay()
}
// 定义空接口类型(可以代表任何数据类型)
type kong interface{}
// 定义 ZFB 结构体,代表支付宝
type ZFB struct {
name string
}
// 定义 WX 结构体,代表微信
type WX struct {
name string
}
// 为 ZFB 绑定方法,实现 MoneyPay 接口
func (z *ZFB) pay() {
fmt.Println("this is zfb pay")
}
// 为 WX 绑定方法,实现 MoneyPay 接口
func (w *WX) pay() {
fmt.Println("this is wx pay")
}
// 定义函数 FinPay,接收 MoneyPay 接口类型的参数
func FinPay(p MoneyPay) {
p.pay()
}
func main() {
// 实例化支付宝和微信对象(均为指针类型)
z1 := &ZFB{"支付宝"}
w1 := &WX{"微信"}
// 接口变量可以接收实现该接口的任何对象
var j1, j2 MoneyPay
j1 = z1
j2 = w1
fmt.Println("j1 and j2:", j1, j2)
// 通过接口调用支付方法,实现多态
FinPay(z1)
FinPay(w1)
// 空接口示例,空接口可以保存任意类型的数据
m1 := map[string]kong{
"name": "luobozi",
"age": 18,
}
fmt.Println("空接口 map:", m1)
// 类型断言:将空接口中的数据转换为具体类型,然后进行计算
result := m1["age"].(int) + 10
fmt.Println("断言后计算结果:", result)
}
说明:
- 接口
MoneyPay
定义了pay()
方法,ZFB
和WX
结构体分别通过指针接收者实现了该方法,从而都满足MoneyPay
接口。 - 函数
FinPay
接收接口类型参数,可以接受任何实现了pay()
方法的类型,体现多态性。 - 空接口
kong
可以存放任意数据,通过类型断言可以获取具体类型值。
总结
本篇代码整理涵盖了 Go 语言中“继承和多态”的相关知识点:
继承(结构体嵌入):
- 利用结构体嵌入实现类似继承的效果
- 字段重写示例:子类中定义同名字段覆盖父类字段
- 调用嵌入结构体的方法
多态:
- 通过接口定义多态行为,只要类型实现了接口方法,就能被当作该接口类型使用
- 演示了如何使用空接口存储任意类型数据,并通过类型断言获取具体类型