go语言学习笔记 — 基础 — 基本语法 — 类型相关:类型定义与类型别名
Posted Locutus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go语言学习笔记 — 基础 — 基本语法 — 类型相关:类型定义与类型别名相关的知识,希望对你有一定的参考价值。
类型别名主要用于go代码升级、迁移中,类型的兼容性问题。这解决了代码重构中最麻烦的类型名变更问题。
1. 类型定义与类型别名
- 类型定义
写法:
type TypeDef Type
栗子:
type byte uint8
type rune int32
- 类型别名
写法:
type TypeAlias = Type
栗子:
type byte = uint8
type rune = int32
TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型,就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指向他本人。
2. 区分类型定义与类型别名
- 类型别名与类型定义的区别
类型别名与类型定义表面上看只有一个等号的差异,实际的区别有
1)将TypeDef类型定义为Type,TypeDef会形成一种新的类型,TypeDef本身依然具备Type的特性
2)将Type类型别名为TypeAlias,使用时,TypeAlias与Type等效
3)TypeAlias类型只会在代码中存在,编译完成时,不会再有TypeAlias类型
package main
import (
"fmt"
"testing"
)
// 把NewInt类型定义为int
type NewInt int
// 为int取一个别名IntAlias
type IntAlias = int
func main() {
// 把a声明为NewInt类型,若此时打印a,值为0
var a NewInt
// 查看a的类型名
// a type: main.NewInt
fmt.Printf("a type: %T\\n", a)
// 将a2声明为IntAlias类型
var a2 IntAlias
// 查看a2的类型名
// a2 type: int
fmt.Printf("a2 type: %T\\n", a2)
}
类型别名与类型定义表面上看只有一个等号的差异,实际的区别有
- 将NewInt类型定义为int,NewInt会形成一种新的类型,NewInt本身依然具备int的特性
- 将int类型别名为IntAlias,使用时,IntAlias与int等效
- a的类型是main.NewInt,表示main包下定义的NewInt类型;a2类型是int。IntAlias类型只会在代码中存在,编译完成时,不会有IntAlias类型。
更多的例子:
package main
import (
"fmt"
)
type (
byte int8
rune int32
文本 string
// byteSize int64 标记业务逻辑对应的数据类型
)
func main() {
var a byte
a = -120
var b 文本 // 文本 == string
b = "中文类型名"
var c rune
c = -50000
fmt.Println(a)
fmt.Println("文本b:" + b)
fmt.Println(c)
}
3. 非本地类型不能添加方法
我们可以使用类型别名,为各种类型起新的名字。是否意味着,我们可以在代码里任意为这些类型添加方法?
package main
import (
"time"
)
// 定义 time.Duration 的别名为 MyDuration
type MyDuration = time.Duration
// 为接收器类型 MyDuration 添加一个方法
func (m MyDuration) EasySet(a string) {
}
func main(){
}
编译器提示:不能在一个非本地的类型time.Duration上定义新方法。
非本地方法指使用time.Duration的方法所在的包,也就是main 包。因为time.Duration是在time包中定义,在main包中使用。time.Duration包与main包不在同一个包中,因此不能给不在一个包中的类型定义方法。
解决这个问题有下面两种方法:
- 将第8行修改为
type MyDuration time.Duration
,也就是将 MyDuration从类型别名改为类型定义 - 将MyDuration的类型别名放在time包中
4. 在结构体成员嵌入时使用别名
类型别名作为结构体嵌入的成员
package main
import (
"fmt"
"reflect"
"testing"
)
// 定义商标结构
type Brand struct {
}
// 为商标结构添加Show方法
func (t Brand) Show() {
}
// 为Brand定义一个别名FakeBrand
type FakeBrand = Brand
// 定义车辆结构
type Vehicle struct {
// 嵌入两个结构
FakeBrand
Brand
}
func main() {
// 声明变量a为车辆类型,即把Vechicle实例化为a
var a Vehicle
// 显式调用结构体FakeBrand的Show方法
// 在调用Show()方法时,证明FakeBrand的本质确实是Brand类型。如果两个类型都有Show()方法,会有歧义。
// 因此只有Brand类型有Show()方法,FakeBrand的本质是Brand类型。
a.FakeBrand.Show()
a.Brand.Show()
// 使用反射,取变量a的类型反射对象,以查看其成员类型
ta := reflect.TypeOf(a)
// 遍历a的所有结构体成员变量
for i := 0; i < ta.NumField(); i++ {
// a的成员信息
f := ta.Field(i)
// 打印Vehicle结构体类型所有成员的字段名和类型
fmt.Printf("FieldName: %v, FieldType: %v\\n", f.Name, f.Type.Name())
}
}
FakeBrand是Brand的一个类型别名。在Vehicle类型中入FakeBrand和Brand,并不意味着嵌入两个Brand。FakeBrand类型会以名字的方式保留在Vehicle的成员中。
如果尝试把a.FakeBrand.Show()改为a.Show(),编译器将发生报错:ambiguous selectora.Show。在调用Show()方法时,证明FakeBrand的本质确实是Brand类型。因为两个类型都有Show()方法,会发生歧义。
以上是关于go语言学习笔记 — 基础 — 基本语法 — 类型相关:类型定义与类型别名的主要内容,如果未能解决你的问题,请参考以下文章
go语言学习笔记 — 基础 — 基本语法 — 类型相关:类型的可比较性
go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的类型零值
go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 常量变量的声明:单个变量的声明与赋值
go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 常量变量的声明:多个变量的初始化声明与赋值