golang类型别名的设计初衷是为了解决代码重构时,类型在包(package)之间转移时产生的问题,类型别名是go1.9新引入的,和原类型完全一样; golang类型定义(或者应该叫类型再定义)的类型和原类型不一样。类型定义一般用在为一个类型添加一个方法使用?也就是方法前面的接受者?

类型别名(go1.9的新特性)

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

​ 我们可以用关键字type声明自定义的各种类型。当然了,这些类型必须在Go语言基本类型和高级类型的范畴之内。在它们当中,有一种被叫做别名类型的类型,如下声明它:

// 为类型string取一个别名
// 这条声明语句表示,MyString是string类型的别名类型。顾名思义,别名类型与其源类型的区别恐怕只是在名称上,它们是完全相同的。
// 源类型与别名类型是一对概念,是两个对立的称呼。别名类型主要是为了代码重构而存在的,
// https://github.com/golang/proposal/blob/master/design/18130-type-alias.md
type MyString = string

​ Go1.9中内建的基本类型中就存在两个别名类型

  • byte是unint8的别名类型
  • rune是int32的别名类型
type byte = uint8
type rune = int32

潜在类型(也叫类型再定义)

​ 下面声明一个类型再定义

// 类型再定义,注意这里没有等号
// MyString2和string就是两个不同的类型了,这里的MyString2是一个新的类型,不同于其他任何类型
// 这种方式也可以被叫做对类型的再定义。我们刚刚把string类型再定义成了另外一个类型MyString2
// 对于这里的类型再定义来说,string可以被称为MyString2的潜在类型。潜在类型的含义是某个类型在本质上是哪个类型或者是哪个类型的集合
// 潜在类型相同的不同类型的值是可以进行类型转换的。因此MyString2类型的值与string类型的值可以使用类型转换表达式进行互转。
// 但是对于集合类的类型[]MyString2与[]string来说这样做却是不合法的,因为[]MyString2与[]string的潜在类型不同,分别是MyString2和string
// 另外,即使两个类型的潜在类型相同,它们的值之间也不能进行判等或比较,它们的变量之间也不能赋值
type MyString2 string

类型别名声明与类型再定义之间的区别

// 定义类型别名
// 类型别名和原类型完全一样,可以随意命名,增加代码可读性,拓展外部访问权限
type newType = oldType
// 定义新类型
type newType oldType

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

​ 还有一个重要的区别在于类型定义的类型的方法集和原始类型的方法集没有任何关系,而类型别名和原始类型的方法集是一样的

别名类型在代码重构过程中可以起到哪些作用

// 别名在代码重构中非常有用,例如以前使用的是p.T这个类型,重构过程中需要把它移到p1.T1,这时只需要在p包中定义type T = p1.T1,这样基本之前使用p.T的代码都不用修改

类型别名存在的意义

  • 名字可以取的更通俗易懂
  • 需要修改数据类型时,只用改定义的那一处地方
  • 可以很方便的添加特有方法,以实现某些接口

型别名主要用在:

  1. 在大规模的重构项目代码的时候,尤其是将一个类型从一个包移动到另一个包中的时候,有些代码使用新包中的类型,有些代码使用旧包中的类型, 比如context
  2. 允许一个庞大的包分解成内部的几个小包,但是小包中的类型需要集中暴漏在上层的大包中

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

如果定义的类型别名是exported (首字母大写)的,那么别的包中就可以使用,它和原始类型是否可exported没关系。也就是说,你可以为unexported类型定义一个exported的类型别名,如下面的例子:

type t1 struct {
	S string
}
type T2 = t1

类型再定义存在的意义


举例

package http

type HandlerFunc func(w ResponseWriter, r *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

// HandlerFunc显示了在Go语言接口机制中一些不同寻常的特点。这是一个有实现了接口http.Handler方法的函数类型。
// ServeHTTP方法的行为调用了它本身的函数。因此HandlerFunc是一个让函数值满足一个接口的适配器(此处是http.Handler
// 接口适配器,因为实现了ServeHTTP方法),这里函数和这个接口仅有的方法有相同的函数签名。实际上,这个技巧让一个单一的
// 类型例如MyHandler以多种方式满足http.Handler接口:一种通过它的list方法,一种通过它的price方法等等。
所以为了方便,net/http包提供了一个全局的ServeMux实例DefaultServerMux和包级别的http.Handle和http.HandleFunc函数。
现在,为了使用DefaultServeMux作为服务器的主handler,我们不需要将它传给ListenAndServe函数;nil值就可以工作。

基于并发考虑,web服务器应该在一个新的协程中调用每一个handler,所以当handler获取其它协程或者这个handler本身的其它请求也可以访问的变量时一定要使用预防措施比如锁机制。