继承的讨论(2)|学习笔记

简介: 快速学习继承的讨论(2)

开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:继承的深入讨论(2)】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/626/detail/9694


继承的讨论(2)

 

内容简介:

一、结构体嵌入两个(或多个)匿名结构体时的继承

二、组合关系

三、创建结构体变量时的继承

 

一、 结构体嵌入两个(或多个)匿名结构体时的继承

1. 说明

结构体嵌入两个(或多个)匿名结构体,如下段代码中有 A 结构体和B 结构体, A 结构体和 B 结构体都被嵌入到 C 结构体中,并且 A结构体有 Name 字段和 Age 字段,而 B 结构体中也有 Name 字段,这样应当如何处理?如果两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),就如下段代码中两个匿名结构体当中有 Name 字段,但是 C 结构体中没有同名的字段或方法( Name ),在这种情况下,访问时就必须明确指定匿名结构体名字,否则编译报错,总的来说如果嵌了两个结构体,且这两个结构体有相同的字段和方法,但是 C 结构体却没有同名的字段或名字,这种时候为了区分要使用 A 结构体的还是 B 结构体的,就必须把匿名结构体的名字带上,否则编译报错。

type A struct {

Name string

age int

}

type B struct {

Name string

Score float64

}

type C struct {

A

B

//Name string

}

2. 代码实现

首先新建一个文件夹称为 extendsdetails2 ,在其中新建一个文件称为 main.go ,并输入以下代码:

package main

import (

“fmt”

)

type A struct {

Name string

age int

}

type B struct {

Name string

Score float64

}

type C struct {

A

B

}

func main() {

var c c

c.Name = “tom”// error

}

3. 编辑器显示” error (错误)”

为什么编译器会显示错误?因为在 c.Name = “tom”// error 访问Name 字段时会根据上文提到的访问规则和顺序来访问,即会先到 C结构体中查找, C 结构体没有 Name 字段就会去嵌入的 A 结构体和B 结构体中去查找,会发现上文代码中 A 结构体内有一个 Name 字段, B 结构体内也有一个 Name 字段,这时它便不知道把 Name 字段给 A 结构体里的 Name 字段还是给 B 结构体的字段,于是就会编译报错。保存以上代码后就会在 c.Name = “tom”// error 段代码中出现编译出错,因为此段代码是模糊不明确的,它并不知道选择器应当如何选择,这时若将此段代码修改为 c.A.Name = “tom”// error ,便明确显示要找 A 结构体内的 Name 字段;假设 Name 字段内就有 Name string ,如以下代码:

type C struct {

A

B

Name string

}

这样也不会报错,因为 C 结构体内本身就有 Name 字段,这时目的很清晰,首先会去  C里查找 Name 字段,若在 C 内找到了便不会去 A 结构体和 B 结构体中去查找,也就不会发生选择器不确定的问题,修改完以上代码之后点击保存,显示有个小错误就是没有用到”fmt”,将以上代码修改为:

package main

import (

“fmt”

)

type A struct {

Name string

age int

}

type B struct {

Name string

Score float64

}

type C struct {

A

B

//Name string

}

func main() {

var c c

//如果 c 没有 Name 字段,而 A 和 B 有 Name ,这时就必须通过指定匿名结构体名字来区分

//所以 c.Name 就会报编译错误,这个规则对方法也是一样的!

c.A.Name = “tom”// error

fmt.Println(“c”)

}

 

二、组合关系

1. 说明

如果一个 struct 嵌套了一个有名结构体,这种模式就是组合,这一般不称之为继承,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字。如以下代码:

type A struct {

Name string

Age int

}

type C struct {

a A

}

这便是组合,在 Golang 里继承是非常灵活的,其中 A 结构体内有Name 和 Age 字段,在 C 中嵌套了一个 A 同时指定了名字,这时若要访问 A 里的 Name ,就必须把名字带上,否则访问不成功。

2. 代码实现

在上文新建的文件 main.go 中继续编写以下代码:

package main

import (

“fmt”

)

type A struct {

Name string

age int

}

type B struct {

Name string

Score float64

}

type C struct {

A

B

//Name string

}

type D struct {

a A //有名结构体

}

func main() {

var c c

//如果c 没有 Name 字段,而 A 和 B 有 Name ,这时就必须通过指定匿名结构体名字来区分

//所以 c.Name 就会报编译错误,这个规则对方法也是一样的!

c.A.Name = “tom”// error

fmt.Println(“c”)

var d D

d.Name = “jack”

}

3. 代码报错

点击保存,保存完后编辑器立即就会报错,它提示 d.Name undefined (type D has no filed or method Name) 也就是说 D 里没有这些方法,因为

type D struct {

a A //有名结构体

}

中的”a A //有名结构体“不是匿名结构体,在检查规则时编辑器会先看 d 对应的类型是否有 Name ,如果没有便直接报错,总的来说因为不是匿名结构体,它就不会往上去找,就直接在 D 里看是否有Name ,如果没有就直接提示 D 中没有 Name 字段,所以在 Golang里承继关系很清晰,如果将 a 去掉,那就不会报错,或者在 d.Name = “jack” 代码中添加 a 也不会报错,即修改为 d.a.Name = “jack”;如果在 D 结构体中添加 Name string ,这样编辑器也是不会报错的。根据上文内容,将上文中的代码修改为正确的代码,如下:

package main

import (

“fmt”

)

type A struct {

Name string

age int

}

type B struct {

Name string

Score float64

}

type C struct {

A

B

//Name string

}

type D struct {

a A //有名结构体

}

func main() {

var c c

//如果 c 没有 Name 字段,而 A 和 B 有 Name ,这时就必须通过指定匿名结构体名字来区分

//所以 c.Name 就会报编译错误,这个规则对方法也是一样的!

c.A.Name = “tom”// error

fmt.Println(“c”)

//如果 D 中是一个有名结构体,则访问有名结构体的字段时,就必须带上有名结构体的名字

//比如 d.a.Name

var d D

d.a.Name = “jack”

}

 

三、创建结构体变量时的继承

1. 说明

嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值。在前面写细节时,比如建一个 pupil ,建完之后再给 Name 和 Age 赋词,很麻烦。因此在 Golang 里可以在直接创建结构体变量时,它直接给各个匿名结构体字段赋值。

2. 代码实现

在之前建的 main.go 文件中改写代码,如下:

package main

import (

“fmt”

)

type A struct {

Name string

age int

}

type B struct {

Name string

Score float64

}

type C struct {

A

B

//Name string

}

type D struct {

a A //有名结构体

}

type Goods struct {

Name string

Price float64

}

type Brand struct {

Name string

Address string

}

type TV struct {

Goods

Brand

}

func main() {

var c c

//如果 c 没有 Name 字段,而 A 和 B 有 Name ,这时就必须通过指定匿名结构体名字来区分

//所以 c.Name 就会报编译错误,这个规则对方法也是一样的!

c.A.Name = “tom”// error

fmt.Println(“c”)

//如果 D 中是一个有名结构体,则访问有名结构体的字段时,就必须带上有名结构体的名字

//比如 d.a.Name

var d D

d.a.Name = “jack”

//嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值

tv := TV{ Goods{“电视机001”, 5000.99}, Brand{“海尔”,“山东”}, }

fmt.Println(“tv”,tv)

}

执行结果为:

D:\goproject\src\go_code\chapter11\extendsdetails2>cd ..

D:\goproject\src\go_code\chapter11>cd extendsdetails2

D:\goproject\src\go_code\chapter11\extendsdetails2>go run main.go

c

tv  <<电视机001 5000.99> <海尔 山东>>

3. 将代码同等转换1

可以将上文中的 tv := TV{ Goods{“电视机001”, 5000.99}, Brand{“海尔”,“山东”}, }写为以下形式(为了方便测试执行结果,修改了名字价格和地址):

tv2 :=  Tv{

Goods{

Prince : 5000.99,

Name : “电视机002”,

},

Brand{

Name : ”夏普”,

Address : “北京”,

},

fmt.Println(“tv2”,tv2)

}

执行结果为:

D:\goproject\src\go_code\chapter11\extendsdetails2>go run main.go

c

tv  <<电视机001 5000.99> <海尔 山东>>

tv2  <<电视机002 5000.99> <夏普 北京>>

4. 嵌套结构体的另种方法

(1)如何嵌套

可以这样去嵌套一个结构体,即在

type TV struct {

Goods

Brand

}

位置下面添加一段新代码:

type TV2 struct {

*Goods

*Brand

}

总的来说在嵌套一个匿名结构体时,也可以嵌套匿名结构体的指针类型,这样它嵌套的就是指针,返回来就是地址,效率更高,下面演示如何使用此种方法(在前文输入的代码后继续输入以下代码):

tv3 := TV2{  &Goods{“电视机003”, 7000.99}, &Brand{“创维”,“河南”}, }

fmt.Println(“tv3”,tv3)

}

执行结果为:

D:\goproject\src\go_code\chapter11\extendsdetails2>go run main.go

c

tv  <<电视机001 5000.99> <海尔 山东>>

tv2  <<电视机002 5000.99> <夏普 北京>>

Tv3  <0xc042002400 0xc042002420>

(2)代码改进

由于前文给的是地址,于是执行结果中显示的是地址,可以修改为:

tv3 := TV2{  &Goods{“电视机003”, 7000.99}, &Brand{“创维”,“河南”}, }

fmt.Println(“tv3”, *tv3.Goods, *tv3.Brand)

}

执行结果为:

D:\goproject\src\go_code\chapter11\extendsdetails2>go run main.go

c

tv  <<电视机001 5000.99> <海尔 山东>>

tv2  <<电视机002 5000.99> <夏普 北京>>

Tv3  <电视机003 7000.99> <创维 河南>

5. 将代码同等转换2

可以将上文中的 tv3 := TV2{  &Goods{“电视机003”, 7000.99}, &Brand{“创维”,“河南”}, }写为以下形式(为了方便测试执行结果,修改了名字价格和地址):

tv4 :=  Tv2{

&Goods{

Name : “电视机002”,

Price : 9000.99,

},

&Brand{

Name : ”长虹”,

Address : “四川”,

},

fmt.Println(“tv4”, *tv4.Goods, *tv4.Brand)

}

执行结果为:

D:\goproject\src\go_code\chapter11\extendsdetails2>go run main.go

c

tv  <<电视机001 5000.99> <海尔 山东>>

tv2  <<电视机002 5000.99> <夏普 北京>>

tv3  <电视机003 7000.99> <创维 河南>

Tv4  <电视机004 9000.99> <长虹 四川>

相关文章
【JavaSE专栏62】继承,JAVA面向对象编程中的一项重要特性
【JavaSE专栏62】继承,JAVA面向对象编程中的一项重要特性
119 0
|
5月前
|
存储 编译器 程序员
【C++高阶】C++继承学习手册:全面解析继承的各个方面
【C++高阶】C++继承学习手册:全面解析继承的各个方面
50 1
|
4月前
|
存储 编译器 C++
C++基础知识(六:继承)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
5月前
|
Java
继承基础知识
继承基础知识
18 0
|
PHP 开发者
继承相关概念和实现|学习笔记
快速学习继承相关概念和实现
继承相关概念和实现|学习笔记
|
网络协议 Java 测试技术
继承的深入讨论(1)|学习笔记
快速学习继承的深入讨论(1)
继承的深入讨论(1)|学习笔记
|
网络协议 Java 测试技术
继承的深入讨论(1) | 学习笔记
快速学习继承的深入讨论(1)
继承的深入讨论(1) | 学习笔记
继承的基础概念
继承的基础概念
78 0
|
程序员 C++
【C++进阶学习】C++中的继承(1)
【C++进阶学习】C++中的继承(1)
【C++进阶学习】C++中的继承(1)
|
编译器 C++
【C++进阶学习】C++中的继承(2)
【C++进阶学习】C++中的继承(2)
下一篇
无影云桌面