1. 自定义类型格式

用户自定义类型使用关键字 type ,其语法格式是:

type newType oldType

oldType 可以是自定义类型、预声明类型、未命名类型中的任意一种。

newType 是新类型的标识符,与 oldType 具有相同的底层类型,并且都继承了底层类型的操作集合(这里的操作不是方法,比如底层类型是 map ,支持 range 迭代访问,则新类型也可以使用 range 迭代访问) 。除此之外, newTypeoldType 是两个完全不同的类型, 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
}
  1. 按照字段顺序进行初始化
// 以下三种写法都可以
a := Person{"Tom", 25}b := Person{"Tom",25}c := Person{"Tom",25,   // 必须加逗号}

不推荐这种写法,一旦结构体增加字段,则需要重新修改初始化语句的顺序。

  1. 指定字段名进行初始化

推荐这种写法,结构体增加字段后,无需修改原有初始化语句的顺序。

a := Person{name:"Tom", age:25}b := Person{name:"Tom", age:25}b := Person{name:"Tom", age:25, // 加逗号
}   // 初始化语句结尾的 } 独占一行,则最后一个字段的后面一定要带上逗号。
  1. 使用 new 创建内置函数,字段默认初始化为其类型的零值, 返回值是指向结构的指针。例如:
p := new(Person)
// 此时 name 为 "", age 为 0

这种方法不常用,一般使用 struct 都不会将所有字段初始化为零值。

  1. 一次初始化一个字段
p := Person{}
p.name = "Tom"
p.age = 25

这种方法不常用,这是一种结构化的编程思维,没有封装,违背了 struct 本身抽象封装的理念。

  1. 使用构造函数进行初始化

这是推荐的一种方法,当结构发生变化时,构造函数可以屏蔽细节。

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 字段}
}
  1. 带有父子关系结构体初始化
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 结构才能设置 RGB 分量,虽然合理但是写法很复杂。使用 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(自定义结构体、结构体初始化、结构体内嵌、自定义接口)相关推荐

  1. ElasticSearch 6.x 学习笔记:12.字段类型

    ElasticSearch 6.x 学习笔记:12.字段类型 欢迎转载. https://blog.csdn.net/chengyuqiang/article/details/79048800 12. ...

  2. Oracle 自定义类型TYPE(subtype, type object, type body, type table) 用法

    原 ORACLE 自定义类型 type/ object 2017年03月28日 13:58:23 tan1012194073 阅读数:5999 版权声明:本文为博主原创文章,未经博主允许不得转载. h ...

  3. 影像组学视频学习笔记(33)-使用SimpleITK实现医学影像差值、Li‘s have a solution and plan.

    作者:北欧森林 链接:https://www.jianshu.com/p/afcd06221ea4 来源:简书,已获转载授权 RadiomicsWorld.com "影像组学世界" ...

  4. 泛型学习笔记:泛型使用的注意点、泛型在继承方面的体现、自定义泛型结构、泛型应用举例、通配符

    泛型学习笔记 集合是为了解决数组某些存储限制的特点出现的,但是数组有一个优点就是存放的数据的类型是确定的,但是集合容器在声明阶段存入的对象是什么类型是不确定的,所以在JDK1.5之前只能把元素的类型设 ...

  5. Lua学习笔记1--基本变量类型

    lua中有8种基础类型:nil(空).boolean(布尔).number(数字).string(字符串).userdata(自定义类型).function(函数).thread(线程)和table( ...

  6. oracle自定义的记录类型,oracle 自定义类型 type / create type

    一:Oracle中的类型有很多种,主要可以分为以下几类: 1.字符串类型.如:char.nchar.varchar2.nvarchar2. 2.数值类型.如:int.number(p,s).integ ...

  7. 《Go学习笔记 . 雨痕》类型

    一.基本类型 清晰完备的预定义基础类型,使得开发跨平台应用时无须过多考虑符合和长度差异. 类型 长度 默认值 说明 bool 1 false   byte 1 0 uint8 int, uint 4, ...

  8. SATA学习笔记 8 ---SATA FIS类型与详细格式解析

    1. FIS简介 SATA执行的所有传输都是以FIS进行的,链路上实际传输的只有:OOB.primitive.FIS 每一个FIS进行传输,都需要完成X_RDY.R_RDY - 的整个交互过程(见SA ...

  9. h5学习笔记之表单类型与属性

    一.智能表单 required: required 内容不能为空 placeholder: 表单提示信息 autofocus:自动聚焦 pattern: 正则表达式 输入的内容必须匹配到指定正则范围 ...

最新文章

  1. arg是什么函数_java后端开发三年!你还不了解Spring 依赖注入,凭什么给你涨薪...
  2. 调试应用不发愁,免安装的 curl 来帮忙
  3. pat和ccf哪个含金量高_函授和网络教育哪个好 哪个含金量高
  4. JAVA基础----java中E,T,?的区别
  5. java websocket ie8_websocket兼容IE8
  6. 【Java】判断学生成绩等级
  7. iPhone ARC 宏定义
  8. linux freopen函数
  9. 数据库连接失败报错com.mysql.cj.jdbc.exceptions.CommunicationsException
  10. 利用Javascript判断操作系统的类型
  11. AR人工智能模型训练计算机,ar人工智能模型训练一般采用什么计算机
  12. mysql group by cube_group by、grouping sets、with rollup、with cube方法
  13. Julia: 关于Array排序函数sortslices
  14. Anylogic学习—银行排队模型
  15. unity粒子编辑器详解
  16. native react 图片裁剪_React Native图片选择裁剪组件
  17. CSS实现文字凹凸效果
  18. android 菜单一行两列,Android RadioGroup 横向显示(两行两列)
  19. 对于《西游记》的看法
  20. Python机器学习06——朴素贝叶斯

热门文章

  1. 通过anaconda2安装python2.7和安装pytorch
  2. Redis 笔记(11)— 文本协议 RESP(单行、多行字符串、整数、错误、数组、空值、空串格式、telnet 登录 redis)
  3. Docker学习(一)-----Docker简介与安装
  4. Ubuntu20.04安装zabbix以及Cannot create the configuration file解决
  5. Windows中配置java变量环境
  6. 微服务架构必备的几点知识
  7. 1-1 机器学习和深度学习综述-paddle
  8. 高级教程: 作出动态决策和 Bi-LSTM CRF 重点
  9. bootstrap上传文件美化
  10. RESTful风格及其SpringMVC实现