目录

如你所知, 类型别名(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
1
2
3
4
5
6
7
8
9
10
package main
import "fmt"
type S = string
func main() {
var s S = "hello world"
fmt.Println(s)
}

当然, 你可以为任意的类型定义类型别名,语言规范中没有限制,可以为数组、结构体、指针、函数、接口、Slice、Map、Channel定义别名,甚至你还可以为通过类型定义(type definition)的类型定义别名,更甚者是你可以为别名定义别名。

func()F
1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import "fmt"
type F = func()
func main() {
var foo F = func() {
fmt.Println("hello type aliases")
}
foo()
}
interface{}G
1
2
3
4
5
6
7
8
9
10
package main
import "fmt"
type G = interface{}
func main() {
var g G = "hello world"
fmt.Println(g)
}
exported
1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import (
"fmt"
"time"
)
type MyTime = time.Time
func main() {
var t MyTime = time.Now()
fmt.Println(t)
}

类型命名和类型声明的区别

记住下面一句话:

类型别名和原类型完全一样,只不过是另一种叫法而已

这句话隐藏着很多的智慧,你可以慢慢体会。

T(x)
vdDvar i I = vI

所以类型别名和类型定义最大的区别在于:类型别名和原类型是相同的,而类型定义和原类型是不同的两个类型。

1
2
3
4
5
6
7
8
9
10
package main
type D = int
type I int
func main() {
v := 100
var d D = v
var 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
import "fmt"
type D = int
func main() {
var v interface{}
var d D = 100
v = d
switch i := v.(type) {
case int:
fmt.Println("it is an int:", i)
// case D:
// fmt.Println("it is D type:", i)
}
}

类型循环

类型别名在定义的时候不允许出现循环定义别名的情况,如下面所示:

1
2
type T1 = T2
type T2 = T1

上面的例子太明显,下面这个例子比较隐蔽,也是循环定义类型别名的情况,当然这些在编译代码的时候编译器会帮你检查,如果出现循环定义的情况会出错。

1
2
3
4
5
type T1 = struct {
next *T2
}
type T2 = T1

可导出性

exportedexportedunexportedexported
1
2
3
4
5
type t1 struct {
S string
}
type T2 = t1

方法集

既然类型别名和原始类型是相同的,那么它们的方法集也是相同的。

T1T3saygreeting
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type T1 struct{}
type T3 = T1
func (t1 T1) say(){}
func (t3 *T3) greeting(){}
func main() {
var t1 T1
// var t2 T2
var t3 T3
t1.say()
t1.greeting()
t3.say()
t3.greeting()
}

如果类型别名和原始类型定义了相同的方法,代码编译的时候会报错,因为有重复的方法定义。

embedded typeT3T1Ss.says.say`不知道该调用还是
1
2
3
4
5
6
7
8
9
10
11
12
13
type T1 struct{}
type T3 = T1
func (t T1) say(){}
type S struct {
T1
T3
}
func main() {
var s S
s.say()
}
time.Time
1
2
3
4
5
6
7
8
9
type NTime = time.Time
func (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
1
2
3
4
5
6
7
8
// 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

参考资料