开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:继承的深入讨论(1)】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9695
继承的深入讨论(1)
内容简介:
一、结构体嵌套匿名结构体
二、匿名结构体访问字段可简化
三、就近访问原则
一、 结构体嵌套匿名结构体
结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或小写的字段、方法,都可以使用。这个与有些编程语言不一样,不能用传统的语言来说,比如 Java , Java 它私有的特性在之类里面用不了,但是在 Golang 里,它一旦把嵌套匿名结构体写进去了,那么嵌套匿名结构体的所有字段和方法不管首字母大写或小写都可以使用,在此举个例子:首先新建一个文件夹称为 extendsdetails ,在其中再建一个文件称为 main.go ,其次将在上一节中输入的代码共有部分复制进 main.go 文件中,并输入以下代码:
package main
import (
“fmt”
)
type A struct {
Name string
age string
}
func (a *A) SayOk() {
fmt.Println(“A SayOk”, a.Name)
}
func (a *A) hello() {
fmt.Println(“A hello”, a.Name)
}
type B struct {
A
}
func main() {
var b B
b.A.Name = “tom”
b.A.age = 19
b.A.SayOk()
b.A.hello()
}
代码执行结果为:
D:\goproject\src\go_code\chapter11\extendsdemo>cd ..
D:\goproject\src\go_code\chapter11>cd extendsdetails
D:\goproject\src\go_code\chapter11\extendsdetails>go run main.go
A SayOk ton
A hello ton
二、匿名结构体可简化
1. 如何简化
匿名结构体字段访问可以简化,如图:
上图是直接将匿名结构体去掉,可以这样做是因为本身 B 的结构体内并没有 Name 、 Age 、 say 、 hello ,因此可以直接跳过。当编辑器在找的时候,会先到 B 结构体内查找是否有 Name 、 Age 、say 、 hello ,若没有便会去查看嵌入的结构体内是否有以上字段,因此在这种情况下,可以简化,比如上文输入的代码完全可以使用简写,即:
将以下代码部分:
var b B
b.A.Name = “tom”
b.A.age = 19
b.A.SayOk()
b.A.hello()
简化为:
var b B
b.Name = “smith”
b.age = 20
b.SayOk()
b.hello()
执行结果为:
D:\goproject\src\go_code\chapter11\extendsdetails>go run main.go
A SayOk ton
A hello ton
A SayOk smith
A hello smith
2. 对疑惑问题的解答
上文代码中的 SayOk 和 hello 明明是和 A 结构体绑定的,为什么可以用 b 就能直接调用?这是因为变量是 B 结构体,程序会先找 B是否有绑定 SayOk 和 hello 的方法,若没有便会去嵌入结构体内去看,随后就会查看 A 是否有 SayOk 和 hello 的方法,如果有便可以直接调用,若没有就直接报错。
3. 对以上代码总结
(1)当我们直接通过 b 访问字段或方法时,其执行流程比如 b.Name 。
(2)编辑器会看 b 对应的类型有没有 Name ,如果有,则直接调用 B类型的 Name 字段。
(3)如果没有就去看 B 中嵌入的匿名结构体 A 有没有声明 Name 字段,如果有就调用,如果没有继续查找,如果都找不到就报错。
(4)打个比方,结构体 A 里面必须的的确确有 Name ,如果把 Name去掉或者没有 Name ,便会报错;如果在结构体 A 中出现了另一个结构体 C ,那么首先会查看 B 结构体中有没有声明 Name 字段,没有便去找匿名结构体 A ,若 A 没有便会找 C 内是否有,以此类推,后面讲到继承的层次时会提到此观点。
三、就近访问原则
1. 何时采用就近访问原则及如何使用
当结构体和匿名结构体有相同的字段或者方法时,编辑器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分。如下图:
以前文中的代码为例,假设结构体 B 内也有 Name string ,同时结构体 B 内也有 SayOk hello 方法,即需添加一段新的代码,如下:
type B struct {
A
Name string
}
func (b *B) SayOk() {
fmt.Println(“B SayOk”, b.Name)
}
func name() {
var b B
b.Name = “jack” //输出ok
b.age = 100 //输出ok
b.SayOk() //输出 B SayOk jack
b.hello() //输出 A hello ? “jack”还是””
}
添加以上代码后,执行结果为:
D:\goproject\src\go_code\chapter11\extendsdetails>go run main.go
B SayOk jack
A hello
2. 对以上代码的讲解
现在 B 内也有 Name 字段,是否会产生冲突?并不会,而是会采用就近原则,也就是说在调用 b.hello 的时候其实是在调
func (a *A) hello() {
fmt.Println(“A hello”, a.Name)
}
中的 hello ,但是此代码写的是 a.Name ,随后编辑器就会去找
type A struct {
Name string
age string
}
中的 A ,但是由于此处的 Name 并没有赋任何值,因此实际上b.hello()便会输出空字符串。
3. 给 A 中的 Name 赋值
若偏想要给 A 中的 Name 赋值,你可以将上文代码中最后一段代码修改为:
func name() {
var b B
b.Name = “jack” //输出ok
b.A.Name = “scott”
b.age = 100 //输出ok
b.SayOk() //输出 B SayOk jack
b.hello() //输出 A hello ? “jack”还是””
}
此时最后的 b.hello() 输出的就是 scott 了,看看执行结果是否正确:
D:\goproject\src\go_code\chapter11\extendsdetails>go run main.go
B SayOk jack
A hello scott
4. 访问 A 中的 SayOk hello
若想去访问 A 里的 SayOk hello ,你只需将上文最后一段代码修改为:
func name() {
var b B
b.Name = “jack” //输出ok
b.A.Name = “scott”
b.age = 100 //输出ok
b.SayOk() //输出 B SayOk jack
b.A.SayOk() //输出 A SayOK scott
b.hello() //输出 A hello ? “jack”还是”scott”
}
执行结果为:
D:\goproject\src\go_code\chapter11\extendsdetails>go run main.go
B SayOk jack
A hello scott
A hello scott