值类型和引用类型
在golang中,同其他语言相同,可以将类型大致分为两类:值类型 与引用类型 。
其中,值类型 存储在栈中,他们在编译期就可以确定变量所占用的空间,并且值类型变量直接指向栈空间的值。使用等号=
将一个变量的值赋给另一个变量时,如j = i
,实际上是在内存中将 i
的值进行了拷贝。我们可以通过 &i
获取变量 i
的内存地址。在golang中,属于值类型的数据类型包含:
结构体:因为是值传递,所以在方法之间传递会进行值复制,如果结构体较大,需要使用指针
而引用类型 拥有更复杂的存储结构,推崇存储在堆内存中,编译时一般无法确定其所占空间:
初始化一系列属性:指针、长度、哈希分布、数据队列等。一个引用类型的变量r1
存储的是r1
的值所在的内存地址(数字),或内存地址中第一个元素所在的位置,这个内存地址被称之为指针,这个指针实际上也被存在另外的某一个变量中。
在golang中,是引用类型的有:
**值类型在传递参数时,进行值拷贝,引用类型则是引用拷贝。**主要是赋值的区别。
类型零值
零值也就是默认值,当一个类型声明之后没有被初始化,那么他的值就是零值,每种类型都有对应的零值,其中,引用类型对应的零值都为nil
:
数据类型 零值 引用类型:slice
, pointer
,map
,channel
,function
,interface
nil详解
在Go语言中,布尔类型的零值(初始值)为 false
,数值类型的零值为 0
,字符串类型的零值为空字符串""
,而指针、切片、映射、通道、函数和接口的零值则是 nil
。
注意,nil是一个值,像0一样,代表不存在
注意,使用nil
,要注意下面几点:
不是关键字和保留字
因为不是关键字和保留字,只是一个标识符,类似int等,所以可以定义一个名称为nil
的变量:
复制 var nil = errors.New( "my god" )
没有默认类型
复制 package main
import (
"fmt"
)
func main () {
fmt.Printf( " %T " , nil ) // .\main.go:9:10: use of untyped nil
print ( nil )
}
引用类型的零值(初始值)都是nil
复制 package main
import (
"fmt"
)
func main () {
var m map [ int ] string
var ptr *int
var c chan int
var sl [] int
var f func ()
var i interface {}
fmt.Printf( " %#v \n" , m) // map[int]string(nil)
fmt.Printf( " %#v \n" , ptr) // (*int)(nil)
fmt.Printf( " %#v \n" , c) // (chan int)(nil)
fmt.Printf( " %#v \n" , sl) // []int(nil)
fmt.Printf( " %#v \n" , f) // (func())(nil)
fmt.Printf( " %#v \n" , i) // <nil>
}
不同引用类型的nil值,指针是相同的
复制 package main
import (
"fmt"
)
func main () {
var arr [] int
var num *int
fmt.Printf( " %p \n" , arr) // 0x0
fmt.Printf( " %p " , num) // 0x0
}
不同引用类型的nil值,占用大小不同
复制 package main
import (
"fmt"
"unsafe"
)
func main () {
var p *struct {}
fmt.Println( unsafe.Sizeof( p ) ) // 8
var s [] int
fmt.Println( unsafe.Sizeof( s ) ) // 24
var m map [ int ] bool
fmt.Println( unsafe.Sizeof( m ) ) // 8
var c chan string
fmt.Println( unsafe.Sizeof( c ) ) // 8
var f func ()
fmt.Println( unsafe.Sizeof( f ) ) // 8
var i interface {}
fmt.Println( unsafe.Sizeof( i ) ) // 16
}
两个nil
字面量比较
复制 package main
import (
"fmt"
)
func main () {
fmt.Println( nil == nil ) // 报错
}
两个值都为nil
的变量不可比较
复制 package main
import (
"fmt"
)
func main () {
// 不同类型变量
var m map [ int ] string
var ptr *int
fmt.Printf(m == ptr) // invalid operation: arr == ptr (mismatched types []int and *int)
}
复制 package main
import (
"fmt"
)
func main () {
// 相同类型变量
var s1 [] int
var s2 [] int
fmt.Printf(s1 == s2) // invalid operation: s1 == s2 (slice can only be compared to nil)
}
变量(有可能是nil 或者 非nil)可以与nil值比较:
复制 package main
import (
"fmt"
)
func main () {
var s1 [] int
fmt.Println(s1 == nil ) // true
}
基本数据类型
数值型
整型
无符号整型
复制 var ui2 uint8 = 1
fmt.Printf( "类型 uint8, 长度= %d 字节 \n" , unsafe.Sizeof(ui2))
var ui3 uint16 = 1
fmt.Printf( "类型 uint16, 长度= %d 字节 \n" , unsafe.Sizeof(ui3))
var ui4 uint32 = 1
fmt.Printf( "类型 uint32, 长度= %d 字节 \n" , unsafe.Sizeof(ui4))
var ui5 uint64 = 1
fmt.Printf( "类型 uint64, 长度= %d 字节 \n" , unsafe.Sizeof(ui5))
有符号整型
数据类型 存储空间(字节) 值范围 数据级别 默认值 -9223372036854775808 ~ 9223372036854775807
复制 var i2 int8 = 1
fmt.Printf( "类型 int8, 长度= %d 字节 \n" , unsafe.Sizeof(i2))
var i3 int16 = 1
fmt.Printf( "类型 int16, 长度= %d 字节 \n" , unsafe.Sizeof(i3))
var i4 int32 = 1
fmt.Printf( "类型 int32, 长度= %d 字节 \n" , unsafe.Sizeof(i4))
var i5 int64 = 1
fmt.Printf( "类型 int64, 长度= %d 字节 \n" , unsafe.Sizeof(i5))
字符整型
复制 // 获取对应字符的ASCII码值
var a byte = 'a'
fmt.Println(a)
// 将字符串转换为Unicode码点
first := "Hello, 世界"
fmt.Println([] rune (first))
表示一个ASCII:
复制 var ch byte = 65
var ch byte = '\x41' // 16进制
var ch byte = 'A'
表示一个Unicode代码点:
复制 var ch byte = '\u0041' // ASCII码值 六十进制41 十进制65 对应大写字母A
fmt.Printf( " %c " , ch) // ASCII对应单个字节,故可以使用byte类型接收
var ch2 rune = '\u266b' // 对应ASCII字符 🎵
fmt.Printf( " %c " , ch2) // 占用四个字节,故使用rune接收
var ch3 int64 = '\U0001F4B8' // 部分ASCII码值(比如生僻汉字),会占用四个字节以上,💸
fmt.Printf( " %c " , ch3) // 所以使用`\U`大写U字母来表示一个Unicode字符,占用8个字节,不够前面补0
浮点型
数据类型 存储空间(字节) 值范围 数据级别 默认值 IEEE-754 1.401298464324817070923729583289916131280e-45 ~ 3.402823466385288598117041834516925440e+38
IEEE-754 4.940656458412465441765687928682213723651e-324 ~ 1.797693134862315708145274237317043567981e+308
复制 fmt.Println( "---------------- 浮点型 ------------------" )
f0 := 3.14
fmt.Printf( "类型推断: %T \n" , f0)
var f1 float32 = 1
fmt.Printf( "类型 float32, 长度= %d 字节 \n" , unsafe.Sizeof(f1))
var f2 float64 = 1
fmt.Printf( "类型 float64, 长度= %d 字节 \n" , unsafe.Sizeof(f2))
// float32可能会有精度丢失,所以通常使用float64
var f3 float32 = 314 e- 2 // 314 / 10^2
fmt.Printf( "指数方式表示: %f \n" , f3)
var f4 float32 = 0.0314 e 2 // 0.0314 * 10^2
fmt.Printf( "指数方式表示: %f \n" , f4)
复数
我们把形如 z=a+bi(a、b均为实数)的数称为复数。其中,a 称为实部,b 称为虚部,i 称为虚数单位。当 z 的虚部 b=0 时,则 z 为实数;当 z 的虚部 b≠0 时,实部 a=0 时,常称 z 为纯虚数。复数域是实数域的代数闭包,即任何复系数多项式在复数域中总有根。
复数的运算:
加法法则:(a+bi)+(c+di)=(a+c)+(b+d)i`
减法法则:(a+bi)-(c+di)=(a-c)+(b-d)i`
乘法法则:(a+bi)(c+di)=(ac-bd)+(bc+ad)i
复制 var name complex128 = complex (x, y)
复数的值由三部分组成 RE + IMi
,其中 RE
是实数部分,IM
是虚数部分,RE
和 IM
均为 float
类型,而最后的 i
是虚数单位。
复数的定义和运算:
复制 var x complex128 = complex ( 1 , 2 ) // 1. 入门+2i
var y complex128 = complex ( 3 , 4 ) // 3+4i
fmt.Println(x + y) // "(4+6i)" 加法运算
fmt.Println(x * y) // "(-5+10i)" 惩罚运算
fmt.Println( real (x * y)) // "-5" 获取实部
fmt.Println( imag (x * y)) // "10" 获取虚部
布尔型
复制 var a bool = true
fmt.Printf( "bool的类型为: %T , 长度为 %d \n" , a, unsafe.Sizeof(a))
字符串
字符串是一个不可变的UTF-8字符序列,并且每个属于ASCII码的字符占用单个字节,其他字符则是占用2-4个字节
不同于C、C++、Java,他们的UTF-8字符长度至少会占用两个字节
Go语言这样做不仅减少了内存和硬盘空间占用,同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码
访问字符
复制 var a string = "Hello 世界"
fmt.Printf( " %c \n" , [] byte (a)[ 0 ]) // H
fmt.Printf( " %c \n" , [] rune (a)[ 7 ]) // 界
字符拼接 "+"
复制 fmt.Println( "a" + "b" ) // 注意,其他类型不会进行字符串转换
多行字符串
多行字符串中的内容都会原样输出,转义字符不会生效。通常用语内嵌代码或者数据
复制 const str = `第一行
第二\n行
第三行`
fmt.Println(str)
输出结果为:
类型转换
数值类型相互转换
复制 // GO语言数据类型转换,只有显示转换(强制类型转换),没有隐式转换
fmt.Println( "----------- 基本数据类型互相转换 -------------" )
var i int = 100
var f float32 = float32 (i)
fmt.Println(f)
// 注意,将大范围值转换为小范围值,可能会造成数据损失
var i1 int64 = 8888
var i2 int8 = int8 (i1)
fmt.Println(i2)
// 赋值左右数据类型一致,否则会报错
var n1 int32 = 12
var n2 int64 = int64 (n1) + 31
fmt.Println(n2)
基本类型 -> string
复制 fmt.Println( "--------------基本数据类型转换为string------------" )
// 使用fmt.Sprintf(), S代表返回string,printf代表格式化打印
var s = fmt.Sprintf( " %d " , 100 )
fmt.Println(s)
r1 := strconv.FormatInt( 678 , 10 ) // 将int数字678转换为10进制字符串
r2 := strconv.FormatInt( 678 , 16 ) // 将int数字678转换为16进制字符串
fmt.Println(r1)
fmt.Println(r2)
// 参数1 要进行format的浮点数
// 参数2 格式化格式,参见API文档
// 参数3 保留小数点为3位
// 参数4 代表该浮点数的bit大小,如果是float64则为64
f1 := strconv.FormatFloat( 10.02 , 'f' , 3 , 64 )
fmt.Println(f1)
r3 := strconv.FormatBool( true )
fmt.Println(r3)
string
->基本类型
复制 fmt.Println( "--------------string转换为基本类型------------" )
b, _ := strconv.ParseBool( "true" )
fmt.Println(b)
i, _ := strconv.ParseInt( "A" , 16 , 64 ) // 将字符串转换, 第二个参数代表该字符串是16进制,第三个值是值数据的类型int64
fmt.Println(i)
f, _ := strconv.ParseFloat( "12.00865" , 64 )
fmt.Println(f)
// 无效类型转换,将会返回对应类型的默认值
b2, _ := strconv.ParseBool( "e" )
fmt.Println(b2)
派生数据类型
派生数据类型又称为复合数据类型,是由基本类型复合而来。
指针
基本类型也有对应的指针类型
复制 *int
*float32
*bool
...
通过&创建指针变量
复制 var i = 100
// 通过取地址符&定义一个指针变量,指针变量通过 *type表示
var ptr *int = & i
// 指针变量是存放指定变量值得内存地址的引用
fmt.Println(ptr) // 0xc00001a0b8
// 初始化指针变量值的时候,初始值一定是地址
// 下面代码会报错
//var b = 89
//var ptrb *int = b
通过*获取指针变量对应的值
复制 fmt.Println( * ptr) // 100,会根据指针变量的地址0xc00001a0b8,找到这个地址对应的内存空间中的值
改变指针变量指向的值
复制 * ptr = 200
fmt.Println(i) // 200
fmt.Println( & i) // 再次查询地址将会发现地址已经发生变化
// 什么类型的指针,接收的一定是相应类型的值,如果不是会报错
// 下面代码会报错
//var c int32 =16
//var ptrc *float32 = &c
通过new()函数创建指针变量
复制 str := new ( string )
* str = "你好"
fmt.Println( * str)
数组
定义数组
复制 // 定义数组变量,不初始化
var balance [ 10 ] float32
// 初始化数组,没有赋值的元素将会赋予类型默认值
balance = [ 10 ] float32 { 0 , 1 , 2 , 3 , 4 , 5 }
fmt.Println(balance)
// 定义并初始化数组
var balance2 = [ 10 ] float32 { 1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
fmt.Println(balance2)
var balance3 [ 10 ] float32 = [ 10 ] float32 { 1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
fmt.Println(balance3)
balance4 := [ 10 ] float32 { 1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
fmt.Println(balance4)
// 定义数组,不显式指定数组长度
balance5 := [ ... ] float32 { 1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
fmt.Println(balance5)
下标访问
复制 // 访问数组元素:下标直接访问
fmt.Println(balance5[ 0 ])
遍历数组:for
复制 // 访问数组元素:遍历数组
for i := 0 ; i < len (balance5); i ++ {
fmt.Printf( " %f " , balance5[i])
if i == len (balance5) - 1 {
fmt.Println()
}
}
遍历数组:for range
复制 // for range 遍历
for index, value := range balance5 {
fmt.Printf( "[ %d ]= %f " , index, value)
if index == len (balance5) - 1 {
fmt.Println()
}
}
数组比较
必须保证数组类型长度相同,才能进行数组比较:
复制 a := [ 2 ] int { 1 , 2 }
b := [ ... ] int { 1 , 2 }
c := [ 2 ] int { 1 , 3 }
fmt.Println(a == b, a == c, b == c) // "true false false"
d := [ 3 ] int { 1 , 2 }
fmt.Println(a == d) // 编译错误:无法比较 [2]int == [3]int
多维数组
复制 // 声明一个二维整型数组,两个维度的长度分别是 4 和 2
var array [ 4 ][ 2 ] int
// 使用数组字面量来声明并初始化一个二维整型数组
array = [ 4 ][ 2 ] int {{ 10 , 11 }, { 20 , 21 }, { 30 , 31 }, { 40 , 41 }}
// 声明并初始化数组中索引为 1. 入门 和 3 的元素
array = [ 4 ][ 2 ] int { 1 : { 20 , 21 }, 3 : { 40 , 41 }}
// 声明并初始化数组中指定的元素
array = [ 4 ][ 2 ] int { 1 : { 0 : 20 }, 3 : { 1 : 41 }}
切片
这个片段可以是整个数组,也可以是指定起始位置的子集
定义切片:从已有数组生成
复制 package main
import "fmt"
func main () {
// 切片建立在一个数组中,需要先定义一个数组
var intarr [ 6 ] int = [ 6 ] int { 3 , 4 , 4 , 1 , 2 , 7 }
// 根据上面的数组,构建一个切片
// 切片的长度是不固定的,所以不需要写切片长度
var slice0 [] int = intarr[ 1 : 3 ] // 这个切面是针对数组intarr的切片,其切取元素位置范围为[1. 入门, 3)
// 输出数组
fmt.Println( "数组为:" , intarr) // [3 4 4 1. 入门 2 7]
fmt.Println( "切片为:" , slice0) // [4 4]
fmt.Println( "切片的容量为:" , cap (slice0)) // 5
}
从开始切到结尾
复制 var slice [] int = intarr[:]
从指定位置切到结尾
复制 var slice [] int intarr[ 2 :]
从开始切刀指定位置
复制 var slice [] int intarr[: 5 ]
重置切片:空切片
复制 var slice [] int intarr[ 0 : 0 ]
定义切片:直接定义
切片声明与数组声明很像,只不过切片的声明没有长度;这种方式声明切片与make()
函数效果一致,底层都会创建一个不可访问的数组
复制 var strList [] string = [] string {} // 声明一个空的切片
定义切片:使用make()函数构造切片
复制 make ( []Type, size, cap ) // 创建一个切片,指定类型、长度、初始容量
a := make ([] int , 2 )
b := make ([] int , 2 , 10 )
fmt.Println(a, b)
fmt.Println( len (a), len (b))
切片容量:cap()
切片长度:len()
添加元素:append()
在使用 append() 函数为切片动态添加元素时,如果空间不足以容纳足够多的元素,切片就会进行“扩容”,此时新切片的长度会发生改变
切片在扩容时,容量的扩展规律是按容量的 2 倍数进行扩充,也就是扩容因子为2
复制 package main
import "fmt"
func main () {
var a [] int
// 在切片尾部追加元素
a = append (a, 1 ) // 追加1个元素
fmt.Println(a)
a = append (a, 2 , 3 , 4 ) // 追加多个元素, 手写解包方式
fmt.Println(a)
a = append (a, [] int { 5 , 6 } ... ) // 追加一个切片, 切片需要解包,三个点代表对数组/切片解包
fmt.Println(a)
// 在切片头部追加元素
a = append ([] int { 0 }, a ... ) // 在开头添加1个元素,
fmt.Println(a)
a = append ([] int { - 3 , - 2 , - 1 }, a ... ) // 在开头添加1个切片
fmt.Println(a)
// 在切片指定位置插入元素
// 在切片第四个位置插入数字100
a = append (a[: 4 ], append ([] int { 100 }, a[ 4 :] ... ) ... )
fmt.Println(a)
}
// 切片可以进行链式调用
复制:copy()
复制需要保证切片类型一致
复制 copy ( destSlice, srcSlice []T) int
slice1 := [] int { 1 , 2 , 3 , 4 , 5 }
slice2 := [] int { 5 , 4 , 3 }
copy (slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy (slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
删除元素
切片的删除实际上仍然是根据当前切片切割,比如删除头部元素:
复制 a = [] int { 1 , 2 , 3 }
a = a[ 1 :] // 删除开头1个元素
a = a[N:] // 删除开头N个元素
删除中间位置:
复制 a = [] int { 1 , 2 , 3 , ... }
a = append (a[:i], a[i + 1 :] ... ) // 删除中间1个元素
a = append (a[:i], a[i + N:] ... ) // 删除中间N个元素
a = a[:i + copy (a[i:], a[i + 1 :])] // 删除中间1个元素
a = a[:i + copy (a[i:], a[i + N:])] // 删除中间N个元素
删除尾部元素:
复制 a = []int{1, 2, 3, ...}
a = append(a[:i], a[i+1:]...) // 删除中间1个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
遍历切片:range
复制 // 创建一个整型切片,并赋值
slice := []int{10, 20, 30, 40}
// 迭代每个元素,并显示其值
for _, value := range slice {
fmt.Printf("Value: %d\n", value)
}
多维切片
复制 var sliceName [][]...[]sliceType
//声明一个二维切片
var slice [][]int
//为二维切片赋值
slice = [][]int{{10}, {100, 200}}
映射
map,键值对映射结构,一种元素对(pair)的无序集合,pair 对应一个 key(索引)和一个 value(值),所以这个结构也称为关联数组或字典,这是一种能够快速寻找值的理想结构,给定 key,就可以迅速找到对应的 value
key的类型不能是:slice
、map
、function
定义map
复制 package main
import "fmt"
func main() {
var m1 map[string]int = make(map[string]int, 10) // map的容量不受限制,该参数10代表此map创建时的初始容量
m1["语文"] = 88
m1["数学"] = 98
fmt.Println(m1) // map[数学:98 语文:88]
m2 := map[string]int{
"语文": 88,
"数学": 98,
}
fmt.Println(m2) // map[数学:98 语文:88]
}
遍历map: range
复制 scene := make(map[string]int)
scene["route"] = 66
scene["brazil"] = 4
scene["china"] = 960
for k, v := range scene {
fmt.Println(k, v)
}
添加/更新键值对
删除键值对: delete()
复制 scene := make(map[string]int)
// 准备map数据
scene["route"] = 66
scene["brazil"] = 4
scene["china"] = 960
delete(scene, "brazil")
for k, v := range scene {
fmt.Println(k, v)
}
清空map
go没有提供清空map的方法,有两种方法可以清空map:
结构体
结构体的定义只是一种内存布局的描述 ,只有当结构体实例化时,才会真正地分配内存。
定义结构体
复制 type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
…
}
type用于自定义类型,定义type可以对结构体进行复用
复制 type Point struct {
X int
Y int
}
同类型变量可将字段放在一行
复制 type Color struct {
R, G, B byte
}
空结构体,没有意义
字段类型不受限制
结构体的字段类型不受限制,可以是基本类型,也可以是复合类型,比如函数等:
复制 // Go program to illustrate the function
// as a field in Go structure
package main
import "fmt"
// Finalsalary of function type
type Finalsalary func(int, int) int
// Creating structure
type Author struct {
name string
language string
Marticles int
Pay int
// Function as a field
salary Finalsalary
}
// Main method
func main() {
// Initializing the fields
// of the structure
result := Author{
name: "Sonia",
language: "Java",
Marticles: 120,
Pay: 500,
salary: func(Ma int, pay int) int {
return Ma * pay
},
}
// Display values
fmt.Println("Author's Name: ", result.name)
fmt.Println("Language: ", result.language)
fmt.Println("Total number of articles published in May: ", result.Marticles)
fmt.Println("Per article pay: ", result.Pay)
fmt.Println("Total salary: ", result.salary(result.Marticles, result.Pay))
}
实例化结构体
基本实例化形式
复制 type Point struct {
X int
Y int
}
var p Point
p.X = 10
p.Y = 20
实例化指针类型结构体(常用)
复制 type Player struct{
Name string
HealthPoint int
MagicPoint int
}
tank := new(Player)
tank.Name = "Canon"
tank.HealthPoint = 300
取地址符的实例化(常用)
对结构体进行&
取地址操作时,视为对该类型进行一次 new 的实例化操作
复制 type Command struct {
Name string // 指令名称
Var *int // 指令绑定的变量
Comment string // 指令的注释
}
var version int = 1
cmd := &Command{}
cmd.Name = "version"
cmd.Var = &version
cmd.Comment = "show version"
初始化结构体
键值类型初始化
复制 type People struct {
name string
child *People
}
relation := &People{
name: "爷爷",
child: &People{
name: "爸爸",
child: &People{
name: "我",
},
},
}
多值列表初始化
复制 package main
import _ "fmt"
func main() {
type People struct {
name string
child *People
}
relation := &People{
"爷爷",
&People{
"爸爸",
&People{
name: "我",
},
},
}
}
匿名结构体的初始化
复制 package main
import (
"fmt"
)
// 打印消息类型, 传入匿名结构体
func printMsgType(msg *struct {
id int
data string
}) {
// 使用动词%T打印msg的类型
fmt.Printf("%T\n", msg)
}
func main() {
// 实例化一个匿名结构体
msg := &struct { // 定义部分
id int
data string
}{ // 值初始化部分
1024,
"hello",
}
printMsgType(msg)
}
管道
参见:并发编程/channel
函数
接口
参照:面向对象/接口
类型定义
复制 // 定义了一个person类型,
type Person struct {
Name string
Age int
}
无论是类型还是类型别名,都可以为他们指定方法
类型别名
类型别名是Go1.9版本添加的新功能,主要用于解决代码升级、迁移中存在的类型兼容性问题。再重构、升级代码时,经常会遇到变量名称变更,C/C++选择使用宏快速定义一段新的代码,而Golang则使用类型别名:
复制 // Go1.9之前,类型byte、rune通过自定义类型定义
type byte uint8
type rune int32
// Go1.9 之后,类型byte、rune则通过类型别名定义
type byte = uint8
type rune = int32
定义类型别名:
复制 // 类型别名定义的格式:
type TypeAlias = Type
// 根据内置类型创建类型别名
type myInt = int64
// 根据自定义类型创建类型别名
type myDuration = time.Duration
// 根据其他类型别名创建类型别名
type myInt2 = myInt
无论是类型还是类型别名,都可以为他们指定方法