Go 学习笔记(33)— Go 自定义类型 type(自定义结构体、结构体初始化、结构体内嵌、自定义接口)
1. 自定义类型格式
用户自定义类型使用关键字 type
,其语法格式是:
type newType oldType
oldType
可以是自定义类型、预声明类型、未命名类型中的任意一种。
newType
是新类型的标识符,与 oldType
具有相同的底层类型,并且都继承了底层类型的操作集合(这里的操作不是方法,比如底层类型是 map
,支持 range
迭代访问,则新类型也可以使用 range
迭代访问) 。除此之外, newType
和 oldType
是两个完全不同的类型, newType
不会继承 oldType
的方法。
无论 oldType
是什么类型,使用 type
声明的新类型都是一种命名类型,也就是说,自定义类型都是命名类型。
type INT int //INT 是一个使用预声明类型声明的自定义类型
type Map map[string]string //Map 是一个使用类型字面量声明的自定义类型
type myMap Map //myMap 是一个自定义类型Map 声明的自定义类型
// INT, Map 、myMap 都是命名类型
2. 自定义 struct 类型
struct
类型是 Go
语言自定义类型的普遍的形式,是 Go
语言类型扩展的基石,也是 Go
语言面向对象承载的基础。
前面章节将 struct
划为未命名类型,那时的 struct
是使用字面量来表示的,如果使用 type
语句声明,则这个新类型就是命名类型。例如:
// 使用 type 自定义的结构类型属于命名类型
type XXXName struct {field1 type1field2 type2
}// errorString 是一个自定义结构类型,也是命名类型
type errorString struct {s string
}// 结构字面量属于未命名类型
struct {field1 type1field2 type2
}// struct{} 是非命名类型空结构
var s = struct{}{}
2.1 struct 初始化
以 Person
结构为例来讲一下结构的初始化的方法。例如:
type Person struct {name stringage int
}
- 按照字段顺序进行初始化
// 以下三种写法都可以
a := Person{"Tom", 25}b := Person{"Tom",25}c := Person{"Tom",25, // 必须加逗号}
不推荐这种写法,一旦结构体增加字段,则需要重新修改初始化语句的顺序。
- 指定字段名进行初始化
推荐这种写法,结构体增加字段后,无需修改原有初始化语句的顺序。
a := Person{name:"Tom", age:25}b := Person{name:"Tom", age:25}b := Person{name:"Tom", age:25, // 加逗号
} // 初始化语句结尾的 } 独占一行,则最后一个字段的后面一定要带上逗号。
- 使用
new
创建内置函数,字段默认初始化为其类型的零值, 返回值是指向结构的指针。例如:
p := new(Person)
// 此时 name 为 "", age 为 0
这种方法不常用,一般使用 struct
都不会将所有字段初始化为零值。
- 一次初始化一个字段
p := Person{}
p.name = "Tom"
p.age = 25
这种方法不常用,这是一种结构化的编程思维,没有封装,违背了 struct
本身抽象封装的理念。
- 使用构造函数进行初始化
这是推荐的一种方法,当结构发生变化时,构造函数可以屏蔽细节。
type Cat struct {Color stringName string
}func NewCatByName(name string) *Cat {return &Cat{Name: name, // 忽略 Color 字段}
}func NewCatByColor(color string) *Cat {return &Cat{Color: color, // 忽略 Name 字段}
}
- 带有父子关系结构体初始化
type Cat struct {Color stringName string
}type BlackCat struct {Cat
}// 基类
func NewCat(name string) *Cat {return &Cat{Name: name, // 忽略 Color 字段}
}// 子类
func NewBlackCat(color string) *BlackCat {cat := &BlackCat{} // 实例化 BlackCat 结构,此时 Cat 也同时被实例化。// 填充 BlackCat 中 嵌入的 Cat 颜色属性。 BlackCat 没有任何成员,所有的成员都来自于 Catcat.Color = colorreturn cat
}
2.2 结构字段的特点
结构的字段可以是任意的类型,基本类型、接口类型、指针类型、函数类型都可以作为 struct
的字段。结构字段的类型名必须唯一, struct
字段类型可以是普通类型,也可以是指针。另外,结构支持内嵌自身的指针,这也是实现树形和链表等复杂数据结构的基础。例如:
// 标准库container/list
type Element struct {// 指向自身类型的指针next, prev *Elementlist *ListValue interface{}
}
2.3 匿名字段
结构体允许其成员字段在声明时没有宇段名而只有类型,这种形式的字段被称为类型内嵌或匿名字段。
在定义 struct
的过程中,如果字段只给出字段类型,没有给出宇段名, 则称这样的字段为“匿名字段”。被匿名嵌入的字段必须是命名类型或命名类型的指针,类型字面量不能作为匿名字段使用。
type Data struct {intfloat32bool
}func main() {d := &Data{int: 10,float32: 3.14,bool: false,}fmt.Printf("%+v", d)
}
类型内嵌其实仍然拥有自己的字段名,只是字段名就是其类型本身而己,,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。
匿名字段的字段名默认就是类型名,如果匿名字段是指针类型,则默认的字段名就是指针指向的类型名。但一个结构体里面不能同时存在某一类型及其指针类型的匿名字段, 原因是二者的字段名相等。如果嵌入的字段来自其他包,则需要加上包名,并且必须是其他包可以导出的类型。
2.4 结构体内嵌
结构体实例化后,如果匿名的字段类型为结构体,那么可以直接访问匿名结构体里的所有成员,这种方式被称为结构体内嵌。
2.4.1 结构体内嵌示例
// 基础颜色
type BasicColor struct {R, G, B float32
}// 完整颜色
type Color struct {// 将基本颜色作为成员Basic BasicColor// 透明度Alpha float32
}func main() {c := new(Color)// 设置基本颜色值c.Basic.B = 1c.Basic.G = 1c.Basic.R = 0c.Alpha = 0.5fmt.Printf("%+v", c)
}
需要通过 Basic
结构才能设置 R
、 G
、 B
分量,虽然合理但是写法很复杂。使用 Go
语言的结构体内嵌写法重新调整代码如下 :
type BasicColor struct {R, G, B float32
}type Color struct {// 将 BasicColor 结构体嵌入到 Color 结构体中, // BasicColor 没有宇段名而只有类型,这种写法就叫做结构体内嵌 。BasicColorAlpha float32
}func main() {c := new(Color)// 可以直接对 Color 的 R、 G 、 B 成员进行设置,编译器通过 Color 的
// 定义知道 R、 G 、 B 成员来自 BasicColor 内嵌的结构体。c.B = 1c.G = 1c.R = 0c.Alpha = 0.5fmt.Printf("%+v", c)
}
2.4.2 结构体内嵌特性
- 内嵌的结构体可以直接访问其成员变量
嵌入结构体的成员,可以通过外部结构体的实例直接访问。如果结构体有多层嵌入结构体,结构体实例访问任意一级的嵌入结构体成员时都只用给出字段名,而无须像传统结构体字段一样,通过一层层的结构体字段访问到最终的宇段。
例如,ins.a.b.c
的访问可以简化为ins.c
。
- 内嵌结构体的字段名是它的类型名
内嵌结构体字段仍然可以使用详细的宇段进行一层层访问,内嵌结构体的字段名就是它的类型名,代码如下:
c := new(Color)c.BasicColor.B = 1c.BasicColor.G = 1c.BasicColor.R = 0
一个结构体只能嵌入一个同类型的成员,无须担心结构体重名和错误赋值的情况,编译器在发现可能的赋值歧义时会报错。
3. 自定义接口类型
接口字面量是非命名类型,但自定义接口类型是命名类型。自定义接口类型同样使用 type
关键字声明。示例如下:
// interface{} 是接口字面量类型标识, 所以 i 是非命名类型交量
var i interface{}
// Reader 是自定义接口类型,属于命名类型
type Reader interface {Read (p []byte) (n int , err error )
}
4. 为什么要使用类型定义
类型定义可以在原类型的基础上创造出新的类型,有些场合下可以使代码更加简洁,如下边示例代码:
package mainimport ("fmt"
)// 定义一个接收一个字符串类型参数的函数类型
type handle func(str string)// exec函数,接收handle类型的参数
func exec(f handle) {f("hello")
}func main() {// 定义一个函数类型变量,这个函数接收一个字符串类型的参数var p = func(str string) {fmt.Println("first", str)}exec(p)// 匿名函数作为参数直接传递给exec函数exec(func(str string) {fmt.Println("second", str)})
}
输出结果:
first hello
second hello
上边的示例是类型定义的一种简单应用场合,如果不使用类型定义,那么想要实现上边示例中的功能,应该怎么书写这段代码呢?
// exec函数,接收handle类型的参数
func exec(f func(str string)) {f("hello")
}
exec
函数中的参数类型,需要替换成 func(str string)
了,咋一看去也不复杂,但是假如 exec
接收一个需要 5 个参数的函数变量呢?是不是感觉参数列表就会很长了。
func exec(f func(str string, str2 string, num int, money float64, flag bool)) {f("hello")
}
从上边的代码可以发现,exec
函数的参数列表可读性变差了。下边再来看看使用类型定义是怎么实现这个功能:
package mainimport ("fmt"
)// 定义一个需要五个参数的函数类型
type handle func(str string, str2 string, num int, money float64, flag bool)// exec函数,接收handle类型的参数
func exec(f handle) {f("hello", "world", 10, 11.23, true)
}func demo(str string, str2 string, num int, money float64, flag bool) {fmt.Println(str, str2, num, money, flag)
}func main() {exec(demo)
}
Go 学习笔记(33)— Go 自定义类型 type(自定义结构体、结构体初始化、结构体内嵌、自定义接口)相关推荐
- ElasticSearch 6.x 学习笔记:12.字段类型
ElasticSearch 6.x 学习笔记:12.字段类型 欢迎转载. https://blog.csdn.net/chengyuqiang/article/details/79048800 12. ...
- Oracle 自定义类型TYPE(subtype, type object, type body, type table) 用法
原 ORACLE 自定义类型 type/ object 2017年03月28日 13:58:23 tan1012194073 阅读数:5999 版权声明:本文为博主原创文章,未经博主允许不得转载. h ...
- 影像组学视频学习笔记(33)-使用SimpleITK实现医学影像差值、Li‘s have a solution and plan.
作者:北欧森林 链接:https://www.jianshu.com/p/afcd06221ea4 来源:简书,已获转载授权 RadiomicsWorld.com "影像组学世界" ...
- 泛型学习笔记:泛型使用的注意点、泛型在继承方面的体现、自定义泛型结构、泛型应用举例、通配符
泛型学习笔记 集合是为了解决数组某些存储限制的特点出现的,但是数组有一个优点就是存放的数据的类型是确定的,但是集合容器在声明阶段存入的对象是什么类型是不确定的,所以在JDK1.5之前只能把元素的类型设 ...
- Lua学习笔记1--基本变量类型
lua中有8种基础类型:nil(空).boolean(布尔).number(数字).string(字符串).userdata(自定义类型).function(函数).thread(线程)和table( ...
- oracle自定义的记录类型,oracle 自定义类型 type / create type
一:Oracle中的类型有很多种,主要可以分为以下几类: 1.字符串类型.如:char.nchar.varchar2.nvarchar2. 2.数值类型.如:int.number(p,s).integ ...
- 《Go学习笔记 . 雨痕》类型
一.基本类型 清晰完备的预定义基础类型,使得开发跨平台应用时无须过多考虑符合和长度差异. 类型 长度 默认值 说明 bool 1 false byte 1 0 uint8 int, uint 4, ...
- SATA学习笔记 8 ---SATA FIS类型与详细格式解析
1. FIS简介 SATA执行的所有传输都是以FIS进行的,链路上实际传输的只有:OOB.primitive.FIS 每一个FIS进行传输,都需要完成X_RDY.R_RDY - 的整个交互过程(见SA ...
- h5学习笔记之表单类型与属性
一.智能表单 required: required 内容不能为空 placeholder: 表单提示信息 autofocus:自动聚焦 pattern: 正则表达式 输入的内容必须匹配到指定正则范围 ...
最新文章
- arg是什么函数_java后端开发三年!你还不了解Spring 依赖注入,凭什么给你涨薪...
- 调试应用不发愁,免安装的 curl 来帮忙
- pat和ccf哪个含金量高_函授和网络教育哪个好 哪个含金量高
- JAVA基础----java中E,T,?的区别
- java websocket ie8_websocket兼容IE8
- 【Java】判断学生成绩等级
- iPhone ARC 宏定义
- linux freopen函数
- 数据库连接失败报错com.mysql.cj.jdbc.exceptions.CommunicationsException
- 利用Javascript判断操作系统的类型
- AR人工智能模型训练计算机,ar人工智能模型训练一般采用什么计算机
- mysql group by cube_group by、grouping sets、with rollup、with cube方法
- Julia: 关于Array排序函数sortslices
- Anylogic学习—银行排队模型
- unity粒子编辑器详解
- native react 图片裁剪_React Native图片选择裁剪组件
- CSS实现文字凹凸效果
- android 菜单一行两列,Android RadioGroup 横向显示(两行两列)
- 对于《西游记》的看法
- Python机器学习06——朴素贝叶斯
热门文章
- 通过anaconda2安装python2.7和安装pytorch
- Redis 笔记(11)— 文本协议 RESP(单行、多行字符串、整数、错误、数组、空值、空串格式、telnet 登录 redis)
- Docker学习(一)-----Docker简介与安装
- Ubuntu20.04安装zabbix以及Cannot create the configuration file解决
- Windows中配置java变量环境
- 微服务架构必备的几点知识
- 1-1 机器学习和深度学习综述-paddle
- 高级教程: 作出动态决策和 Bi-LSTM CRF 重点
- bootstrap上传文件美化
- RESTful风格及其SpringMVC实现