如你所知, 类型别名(type aliases) 最终还是加入到Go 1.9中, Go 1.9 beta2今天已经发布了, 正式版预计8月初发布, 是时候深入了解一下它的新特性了,本文介绍的就是它的重要的新特性之一: 类型别名。
当然,如果你想尝试这些新特性,需要安装Go 1.9的版本,目前是beta2版,可以在官方网站下载。
类型别名主要解决什么问题,为什么需要这个特性? Russ Cox 的论文Codebase Refactoring (with help from Go)介绍了它的背景。类型别名主要用在:
context
类型别名
类型别名的语法如下:
1
type identifier = Type
identifierType=
stringSS
12345678910
package mainimport "fmt"type S = stringfunc main() {var s S = "hello world"fmt.Println(s)}
当然, 你可以为任意的类型定义类型别名,语言规范中没有限制,可以为数组、结构体、指针、函数、接口、Slice、Map、Channel定义别名,甚至你还可以为通过类型定义(type definition)的类型定义别名,更甚者是你可以为别名定义别名。
func()F
12345678910111213
package mainimport "fmt"type F = func()func main() {var foo F = func() {fmt.Println("hello type aliases")}foo()}
interface{}G
12345678910
package mainimport "fmt"type G = interface{}func main() {var g G = "hello world"fmt.Println(g)}
exported
12345678910111213
package mainimport ("fmt""time")type MyTime = time.Timefunc main() {var t MyTime = time.Now()fmt.Println(t)}
类型命名和类型声明的区别
记住下面一句话:
类型别名和原类型完全一样,只不过是另一种叫法而已
这句话隐藏着很多的智慧,你可以慢慢体会。
T(x)
vdDvar i I = vI
所以类型别名和类型定义最大的区别在于:类型别名和原类型是相同的,而类型定义和原类型是不同的两个类型。
12345678910
package maintype D = inttype I intfunc main() {v := 100var d D = vvar i I = v}
type Tnamed Tunderlying
TnamedTunderlying*Tnamed*Tunderlyingchan Tnamedchan Tunderlyingfunc(Tnamed)func(Tunderlying)interface{ M() Tnamed }interface{ M() Tunderlying }
type T1 = T2
T1T2*T1*T2chan T1chan T2func(T1)func(T2)interface{ M() T1 }interface{ M() T2 }
还有一个重要的区别在于类型定义的类型的方法集和原始类型的方法集没有任何关系,而类型别名和原始类型的方法集是一样的,下面再介绍。
既然类型别名和原类型是相同的,那么在`switch - type中,你不能将原类型和类型别名作为两个分支,因为这是重复的case:
123456789101112131415161718
package mainimport "fmt"type D = intfunc main() {var v interface{}var d D = 100v = dswitch i := v.(type) {case int:fmt.Println("it is an int:", i)// case D:// fmt.Println("it is D type:", i)}}
类型循环
类型别名在定义的时候不允许出现循环定义别名的情况,如下面所示:
12
type T1 = T2type T2 = T1
上面的例子太明显,下面这个例子比较隐蔽,也是循环定义类型别名的情况,当然这些在编译代码的时候编译器会帮你检查,如果出现循环定义的情况会出错。
12345
type T1 = struct {next *T2}type T2 = T1
可导出性
exportedexportedunexportedexported
12345
type t1 struct {S string}type T2 = t1
方法集
既然类型别名和原始类型是相同的,那么它们的方法集也是相同的。
T1T3saygreeting
1234567891011121314151617
type T1 struct{}type T3 = T1func (t1 T1) say(){}func (t3 *T3) greeting(){}func main() {var t1 T1// var t2 T2var t3 T3t1.say()t1.greeting()t3.say()t3.greeting()}
如果类型别名和原始类型定义了相同的方法,代码编译的时候会报错,因为有重复的方法定义。
embedded typeT3T1Ss.says.say`不知道该调用还是
12345678910111213
type T1 struct{}type T3 = T1func (t T1) say(){}type S struct {T1T3}func main() {var s Ss.say()}
time.Time
123456789
type NTime = time.Timefunc (t NTime) Dida() {fmt.Println("嘀嗒嘀嗒嘀嗒嘀嗒搜索")}func main() {t := time.Now()t.Dida()}
cannot define new methods on non-local type time.Time
byte 和 rune 类型
byteuint8runeint32
12345678
// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is// used, by convention, to distinguish byte values from 8-bit unsigned// integer values.type byte = uint8// rune is an alias for int32 and is equivalent to int32 in all ways. It is// used, by convention, to distinguish character values from integer values.type rune = int32