Go语言的类型系统

2018-03-19

Golang是一种静态类型的编程语言

  • 编译器需要在编译时知晓程序里每个值的类型
  • 值的类型为编译器提供值的内存空间大小与类型

用户定义类型

  • type [类型名] struct 后跟大括号创建结构类型
  • var [变量名] [类型名] 初始化关键字后,使用结构类型初始化变量,并初始化其中每个字段为零值
  • 声明结构类型赋值给变量,可以用字面量方法有kv对应或只有v方法,要对应定义结构的顺序
  • 可以使用结构字面量作为一个字段的值
  • 可以基于已有类型创建新的类型,但是新创建的和基于原有的并不相同,存在差异
  • (编译器不对同类型的值做隐式转换)

    类型的方法

  • 可以根据类型绑定方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    type user struct{  
    name string
    age int
    }
    func (u user) notify(){
    //值接收者方法
    }

    func (u *user) sendmail(){
    //指针接收者方法
    }
  • 接收者方法分为值接收者方法指针接收者方法
  • func 关键字 与[函数名]之间的参数,被称为接收者,将函数与接收者的类型绑定在一起,如果一个函数存在接收者,此函数被称之为方法
  • 如果使用值接收者方法,调用时使用这个值的副本来执行
  • 也可以使用指向类型的指针来调用值接收者的方法
1
2
3
4
5
6
7
8
9
10
//使用类型赋值值来调用**值接收者方法**  
a := user{"bill",2}
a.notify()
//使用指向类型的指针来调用**值接收者方法**
a := &user{"bill",2}
a.notify()
//相当于
(*a).notify
//测试后并不会改变原值
//除非是指针接收者方法,值接收传入指针
  • 总而言之:值接收者使用值的副本来调用方法,而指针接收者使用实际值来调用方法
  • 也可以使用一个值来调用指针接收者方法
  • Go语言既允许使用值,也允许使用指针来调用方法,不必严格符合收这的类型。这个支持非常方便开发者编写程序。

类型的本质

  • 如果要创建新的值,该类型的方法就使用值的接收者;
  • 如果要修改原有值,就使用指针接收者;
  • 本质是按值传递还是指针,**不要关注某个方法是如何处理这个值,而是要关注值的本质是什么;
  • 切片、映射、通道、接口、函数类型、字符串为引用类型
  • 创建变量被称作“标头(header)值”,每个引用类型创建的标头是包含一个指向底层数据结构的指针。每个引用类型还包含独特的字段,用于管理底层数据结构。因为标头值是赋值而设计的,所以永远不需要共享一个引用类型的值。标头值包含一个指针,因此通过复制来传递一个引用类型的值的副本,本质上就是在共享底层数据结构。
  • 一个值的接收者,正像预期的那样通过复制来传递引用,从而不需要通过指针来共享引用类型的值,此方法在创建函数时同理。
  • 结构描述一组数据值,值的本质既可以是原始的也可以是非原始的
  • 在非原始情况下,对类型的值做增加或者删除应该更改值本身,在程序的其他地方,需要使用指针来共享这个值

接口

  • 多态是代码可以根据理想类型的具体实现采用不同行为的能力
  • 如果类型实现了某个接口,所有使用这个接口的地方都支持这种类型的值
  • 接口是用来定义行为的类型
  • 这些被定义的类型,不由接口直接实现,而是通过方法由用户实现定义的类型实现。
  • 如果用户定义的类型值实现了某个接口类型声明的一组方法,那么这个用户定义的类型的值就可以赋给这个接口类型的值。这个赋值会把用户定义的类型的值存入接口类型的值
  • 对接口方法的调用会执行接口值里面存储的用户定义的类型的值对应的方法,因为任何用户定义的类型都可以实现任何接口,所以对接口值方法的调用自然就是一种多态。在这个关系中用户定义的类型通常为实体类型,原因是如果离开内部存储的用户定义的类型值的实现,接口并没有具体的行为
  • 接口方法集规则
1
2
3
4
接收方法 值
(t T) T and *T
(t *T) *T
//传入时需要 abc(&t) 使用&进行取址

嵌入类型

  • 将已有的类型直接生命在结构类型里;
  • 内部类型包括其方法可以提升至外部(若无,已有的不会提升)

公开或未公开的标识符

  • 当要写的代码属于某个包时,好的时间是使用与代码所在文件夹一样名字作为包名(最佳实践)
  • 标识符小写–未公开,包外的代码不可见。标识符大写–公开,包外可见。
  • 工厂函数: 创建未公开的类型值,返回给调用者
  • 类型内部必须大写才能外部可见

小结

接口是声明了一组行为并支持多态的类型
嵌入类型提供了扩展类型的能力,无需使用继承
标识符要么是从包里公开的,要么是在包里未公开的


Comments: