在go语言中, 定义类型有两种方式, 一种是定义一个全新的类型, 一种是定义一个类型的别名.
语法区别 定义全新的数据类型
定义类型的别名
两者语法上的区别仅仅是有没有=
赋值区别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package mainimport "fmt" type A int type B = int func main () { var number int = 10 var a A var b B a = number b = number fmt.Println(a, b) } `` Goland IDE 会有如下提示: ![](https: 不用编译, IDE就告诉你这么搞不可以了. 所以定义新类型和别名, 在赋值上的区别是, 新的类型需要显式的类型转换. 而别名不需要 以下是正确的代码: `` `go package main import "fmt" type A int type B = int func main() { var number int = 10 var a A var b B // 需要类型转换才可以赋值 a = A(number) b = number fmt.Println(a, b) }
使用区别 如果你给一个非本地的数据类型定义方法时, 编译时会报错, IDE也会提示你错误
1 2 3 4 5 6 7 8 9 10 package mainimport "fmt" type A int type B = int func (b B) sayHi () { }
错误提示: Cannot define new methods on non-local type ‘builtin.int’
但是如果你用的是你自定义的数据类型的别名, 是完全可以的
1 2 3 4 5 6 7 8 9 type C struct { } type MiniC = Cfunc (mc MiniC) sayHi () { fmt.Println("Hi" ) }
别名不仅拥有原数据类型的全部方法, 而且在别名中定义的方法, 会自动出现在原数据类型的方法中, 原类型也可以正常调用别名定义的方法. 因为别名的本质, 就是同一数据类型, 只是名字不同罢了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport "fmt" type C struct { } func (c C) sayHello () { fmt.Println("Hello" ) } type MiniC = Cfunc (mc MiniC) sayHi () { fmt.Println("Hi" ) } func main () { var c C var mc MiniC c.sayHello() c.sayHi() mc.sayHello() mc.sayHi() }
执行结果:
而定义全新类型则跟别名的情况完全不同. 定义全新的类型, 不管你用的是不是本地类型, 都可以为新的类型添加方法. 以下操作是合法的
1 2 3 4 5 6 type A int type B = int func (a A) sayHi () { }
在全新类型下定义的方法, 不会出现在原类型上. 原类型的方法也不会出现在新类型中.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 type C struct { } func (c C) sayHello () { fmt.Println("Hello" ) } type MicroC Cfunc (mc MicroC) sayHi () { fmt.Println("Hi" ) } func main () { var c C var mc MicroC c.sayHello() c.sayHi() mc.sayHello() mc.sayHi() }
执行结果:
1 2 ./a2.go:25:3: c.sayHi undefined (type C has no field or method sayHi) ./a2.go:27:4: mc.sayHello undefined (type MicroC has no field or method sayHello)
新类型的应用场景 在使用有限状态机的情况下, 我们可以为能预见的所有状态定义为一个单独的类型. 拿一台kvm的虚拟机来说. 描述虚拟机的运行状态, 就是一种有限状态机的场景. 虚拟机可以是如下任意一种状态:
up
down
poweringup
poweringdown
rebooting
shutingdown
再比如给虚拟机创建的磁盘文件, 可以有以下格式可选:
我们拿磁盘来举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport "fmt" type DiskFormat string const ( DiskFormatCow DiskFormat = "cow" DiskFormatRaw DiskFormat = "raw" ) func (df DiskFormat) IsValid () bool { switch df { case DiskFormatCow: return true case DiskFormatRaw: return true default : return false } } func createDisk (format DiskFormat) { fmt.Println(format.IsValid()) fmt.Printf("传递的磁盘格式为: %T\n" , format) fmt.Println("创建磁盘文件" ) fmt.Println("磁盘创建完成" ) } func main () { var diskType = "a" createDisk(diskType) }
执行结果:
1 ./a1.go:33:12: cannot use diskType (type string) as type DiskFormat in argument to createDisk
创建磁盘传参的时候, 我传递了一个string "a"
, 但是实际参数需要的数据类型为DiskFormat
, 如果你非要使用自定义字符串传参, 那么你需要类型转换, 就像以下这样:
1 2 3 4 func main () { var diskType = "a" createDisk(DiskFormat(diskType)) }
但是更推荐的做法是使用预定义的常量来传参
1 2 3 func main () { createDisk(DiskFormatCow) }
别名应用场景 别名的一个典型的应用场景是重构项目. 重构项目时, 可能会有代码使用老包中的方法, 也可能会使用扩展的新包中的方法. 使用别名可以灵活的”继承”原数据类型的所有方法, 并做扩展.
总结 别名的本质是一个数据类型的不同的名字. 比如我有一个中文名叫极地瑞雪
, 我还有一个英文名叫PolarSnow
, 英文名就是中文名的一个别名而已, 实际本尊只有一个. 极地瑞雪
新学了个滑雪
的技能, PolarSnow
新学了个游泳
的技能. 本质上, 就是我学会了 滑雪
和 游泳
, 极地瑞雪
和PolarSnow
都可以施展这两项技能
定义全新类型真的就是全新类型, 不夹带任何原数据类型的方法. 就好比是我的克隆人, 从头培养, 从吃饭睡觉打豆豆开始培养, 我现在已有的任何技能, 都和克隆人没有任何关系. 克隆人学会了特殊技能也跟我本尊没有任何关系. 注意, 我的吃饭方法和克隆人的吃饭方法可以同时存在, 各自有各自的吃饭方法. 但是别名是不允许同名方法的情况存在的