既然要讲类型转换,那么我们就要先了解有哪些类型。也就是要先大概了解一下 go 的类型系统(Type system)

首先我们要先知道 go 自带的有哪些类型,这种类型我们一般称为 底层类型(Underlying Type),我们可以看 refelct 包的源码

var kindNames = []string{
	Invalid:       "invalid",
	Bool:          "bool",
	Int:           "int",
	Int8:          "int8",
	Int16:         "int16",
	Int32:         "int32",
	Int64:         "int64",
	Uint:          "uint",
	Uint8:         "uint8",
	Uint16:        "uint16",
	Uint32:        "uint32",
	Uint64:        "uint64",
	Uintptr:       "uintptr",
	Float32:       "float32",
	Float64:       "float64",
	Complex64:     "complex64",
	Complex128:    "complex128",
	Array:         "array",
	Chan:          "chan",
	Func:          "func",
	Interface:     "interface",
	Map:           "map",
	Ptr:           "ptr",
	Slice:         "slice",
	String:        "string",
	Struct:        "struct",
	UnsafePointer: "unsafe.Pointer",
}

可以看到大都是我们常用的类型,这些就是自带的底层类型。这些底层类型每个都是 type, 但是为了和可以自定义的类型区分,它们又都是一个 kind。底层类型也是有细分的,

比如基本类型(basic type)和组合类型(compsite type)

基本类型就是如 int, string,float 这种单一的数据类型,而组合类型就是由基本类型组合来的,如 struct 的各种 filed,map,array,slice 等的 key-value, func 的 signature 等都是由基本类型或其他组合类型组成的。

由于 go 中 int 类型比较复杂,这里单独来讲一下

首先来看 官方文档 中是怎么说的:

uint8       the set of all unsigned  8-bit integers (0 to 255)
uint16      the set of all unsigned 16-bit integers (0 to 65535)
uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)
int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
float32     the set of all IEEE-754 32-bit floating-point numbers
float64     the set of all IEEE-754 64-bit floating-point numbers
complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts
byte        alias for uint8
rune        alias for int32
The value of an n-bit integer is n bits wide and represented using two's complement arithmetic.
There is also a set of predeclared numeric types with implementation-specific sizes:
uint     		either 32 or 64 bits
int      		same size as uint
uintptr  		an unsigned integer large enough to store the uninterpreted bits of a pointer value

首先 int 类型有两个要点,一是有无符号,二是所占字节打小

有符号:int, int8, int16, int32, int64

无符号:uint, uint8, uint16, uint32, uint64

所占字节大小,就是 int后数字大小,如 int32 就占 32 比特

比较特殊的 int 和 uint 类型,是动态的,为了提高可移植性和兼容性,物理机是多少比特,int 和 uint 就是多少比特,如 机器是 32 比特,那 int 就占 32 比特,机器是 64 直接,那 int 就占 64 比特

又比如静态类型(static type)和动态类型(dynamic type)

除了 interface 以外的类型都是静态类型,只有 interface 是动态类型,我们之前说过了,interface 是一组 method 的集合,如果其他任意类型的方法集是此接口方法集的超集,那么此类型就自动实现(implementation)了这个 interface, 那么此类型的实例就能赋值给此接口的实例。我们之前也发现,接口实例的 type-value 和类型实例的 type-value 是一样的,而且接口实例的 key-value 还可以变,所以 interface 就是动态类型

我们谈完了自带的底层类型后,继续来谈一下可以自定义的类型,这种类型叫 defined type ,自然,自带的底层类型就叫 undefined type

要谈自定义类型,就要接触 type 这个关键字

首先我们来看代码

type myInt int
type myInts []myInt

func main() {
	m := myInts(10)
	ms := myInts{10, 20, 30}
	fmt.Println(m)
	fmt.Println(ms)
}

我们可以发现, myInt 实际上底层是 int 类型,而 myInts 则是 int slice 类型,它们可以是底层类型组合,也可以是自定义类型组合,但最终它们都是由底层类型组合的。而我们又说过,底层类型是一种 kind, 故自定义类型不是 kind。

我们在上面讲解了 reflect 包中的 Typeof 和 Valueof 函数用于获取变量的 Type 和 Value,而我们现在知道 Type 又有许多种类,所以 reflect 包也提供了一个 kind 函数用于查看变量的底层类型

源码如下

func (v Value) Kind() Kind {
	return v.kind()
}
type Type interface {
	Kind()
	//
}

我们来实际运用一下

func main() {
	a := 0
	fmt.Println(reflect.Typeof(a).Kind())
	fmt.Println(reflect.Valueof(a).Kind())
}

可以看到两个方法都得到了 int 这个底层类型。

我们刚刚说 type 关键字可以自定义类型,其他它还可以给类型自定义一个名字(type alias),当然类型是不变的。

如 go 内置的

type rune = int32
type byte = uint8

这是为了便于人类记忆,如同导包时 alias 包名,或给字符串或其他表达式建为常量,如

const iota = 0