go语言学习之type浅谈

转自:微点阅读  https://www.weidianyuedu.com

go语言中,type用于类型定义(type definition)与类型别名(type alias)。这两者的差别从名字上已经可以初见端倪。

  类型定义即定义新类型,是一个全新的类型,但可能与被定义类型存在一些关系,如类型转换,方法继承等。类型定义在各语言中有不同的体现,如Java是用class, interface等关键字作类型定义,在python中也是使用class关键字。只不过go为了简洁的原则,可以省些关键字,因而与类型别名重用了type关键字。

  类型别名则是对被定义类型的别称,与其是相同的类型,只不过取了另外一个名字而已。类型别名是本文讨论的重点。这里有几个问题,一是为何go要引入类型别名,其有什么好处?为什么Java或者其他语言没有类型别名?

  在go语言中,type有5种作用,罗列如下。但在本文中,只讲述类型定义与类型别名。

  1. 定义接口
  2. 定义结构体
  3. 类型定义
  4. 类型别名
  5. 类型查询

类型定义

 
  1. package main

  2. import "fmt"

  3. func main() {

  4. h := House{height: 1.0, width: 2.0}

  5. fmt.Println(h.Height())

  6. wh := WhiteHouse{height: 1.0, width: 2.0}

  7. fmt.Println(wh.Area())

  8. w := House(wh) //类型转换

  9. fmt.Println(w.Width())

  10. f1 := Factory{height: 1.0, width: 2.0}

  11. f2 := Factory2{name: "test"}

  12. fw := House(f1)

  13. fmt.Println(fw)

  14. fw2 := House(f2) //运行时这里会报类型转换错误

  15. fmt.Println(fw2)

  16. }

  17. type House struct {

  18. height float32

  19. width float32

  20. }

  21. func (h *House) Height() float32 {

  22. return h.height

  23. }

  24. func (h *House) Width() float32 {

  25. return h.width

  26. }

  27. type WhiteHouse House //类型定义

  28. func (h *WhiteHouse) Area() float32 {

  29. return h.height * h.width

  30. }

  31. type Factory struct {

  32. height float32

  33. width float32

  34. }

  35. type Factory2 struct {

  36. name string

  37. }

  WhiteHouse与House是两个不同的类型,方法Area只属于WhiteHouse。 WhiteHouse可以转换为House类型。但对于类型Factory与Factory2,House与WhiteHouse都只能与Factory进行类型转换(尽管这种类型转换在实践中基本不会出现),而与Factory2进行类型转换则会报错。个中原因在于struct类型的内存布局是按顺序存储的,因为只要类型拥有的属性一致,则进行类型转换不会报运行时错误。既然是顺序存储,那么适当合理地安排不同类型属性的出现顺序,可以达到优化内存使用的目的。比如将几个可以计算出占用内存和为8、16、32、64的属性放在一起,当然内存对齐的位数因不同平台和编译器而异。

类型定义通常还用在定义函数类型上,示例如下:

 
  1. type Handler func(name string)

  2. func process(h Handler) {

  3. h("test")

  4. }

类型别名

在go语言中,类型别名的定义示例如下:

 
  1. package main

  2. import "fmt"

  3. func main() {

  4. h := House{height: 1.0, width: 2.0}

  5. fz := Fangzi{height: 1.0, width: 2.0}

  6. fmt.Println(h.Area()) //House也有了Area方法

  7. fmt.Println(fz.Area())

  8. }

  9. type House struct {

  10. height float32

  11. width float32

  12. }

  13. func (h *House) Height() float32 {

  14. return h.height

  15. }

  16. func (h *House) Width() float32 {

  17. return h.width

  18. }

  19. type Fangzi = House //类型别名

  20. func (f *Fangzi) Area() float32 {

  21. return f.height * f.width

  22. }

  在语法上,类型别名与类型定义的差别仅仅是一个=号。但从上述示例可知,对Fangzi的改变,同样会体现在House上。Fangzi添加了Area方法,同时House也获得了计算Area的能力。这一点与类型定义是明显不同的。

  从这里可以看出,类型别名的一个好处在于可以为不喜欢的类型取一个漂亮的名字。比如,如果你更喜欢Human多于Person的话。

 
  1. type Person struct {

  2. name string

  3. age int

  4. sex string

  5. }

  6. type Human = Person

  除此之外呢?对于库开发者来说,新旧版本包的迁移应是比较常见的事,特别是大版本升级的时候。那么为了保证新旧之间的兼容,可能就会用到类型别名的特性。比如旧包中的com.eventer/lib/AI,大版本升级时,需要变成com.eventer/lib2/AI。如果直接去掉lib/AI,则会造成使用这个库的项目,在引用新包时需要修改所有引用了旧包AI类型的代码。因而更好的作法是在移除com.eventer/lib/AI原有代码之后,加上一个类型别名的定义,即可完美解决这个问题。

 
  1. package com.eventer/lib

  2. import "com.eventer/lib2"

  3. type AI = lib2.AI

  还有吗?如果下面的示例也能被称作一种用途的话,那么这一种用法就是"导出未被导出的类型"

 
  1. type house struct {

  2. name string

  3. height float32

  4. width float32

  5. }

  6. func (h *house) getName() string {

  7. return h.name

  8. }

  9. func (h *house) GetArea() string {

  10. return h.height * h.width

  11. }

  12. type House = house

  但我认为这还是跟前文所述的类型别名的标准用法一般无异,实在看不出有什么不同,除了单词与原类型相同而已。否则根本没必要这样写代码,直接把house声明为House不是更直截了当?除非是用于取别名。

  如果go有泛型,那么类型别名在简化代码,提高代码可读性方面,无疑将带来极大的好处。特别是对于java语言那种泛型的复杂度让人抓狂的语言,只可惜java没有语言层面的类型定义语法。虽然可以通过一些取巧的方式也能达到类似的效果,比如继承。比如下面的例子:

 
  1. public class LongArrayList extends ArrayList<Long> {

  2. }

  上述代码定义一个Long类型的动态数组,相当于为泛型的ArrayList定义了一个新类型,在代码中可以直接使用。这是采用代码技巧突破在java中不能在顶级(top level)定义类型别名的限制,但 通过定义一个类来完成这件事情,是不是太重量级了。再比如:

 
  1. class Test<I extends Integer> {

  2. <L extends Long> void x(I i, L l) {

  3. System.out.println(i.intValue() + ", " + l.longValue());

  4. }

  5. }

  这是在类级别或方法级别定义类型别名。上述例子是通过给Integer、Long定义更短的名字——I和L。

  反正这两种都只是语法形式上的变通,通常没人这样用。

  而在C语言中,有同样的功能typedef关键字,比如:

 
  1. typedef struct Name1 {

  2. elemtype ElemName;

  3. } Name2, Name3;

其中Name1为结构体名,同时它还有两个"外号":Name2,Name3,因为c没有对象的概念,所以很明显这里的typedef并没有go的type关键字的内容多,但他们底层的原理应是相同的,都是通过在在编译时期替换完成。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值