Go 语言允许用户定义类型。当用户声明一个新类型时,这个声明就给编译器提供了一个框 5 第 5 章 Go 语言的类型系统 架,告知必要的内存大小和表示信息。声明后的类型与内置类型的运作方式类似。Go 语言里声 明用户定义的类型有两种方法。最常用的方法是使用关键字 struct,它可以让用户创建一个结 构类型。结构类型通过组合一系列固定且唯一的字段来声明,如下代码清单 所示。结构里每个字段 都会用一个已知类型声明。这个已知类型可以是内置类型,也可以是其他用户定义的类型。
// user 在程序里定义一个用户类型
type user struct {
name string
email string
ext int
privileged bool
}
// 声明 user 类型的变量
var bill user
代码清单 5-3 展示了如何声明一个 user 类型的变量,并使用某个非零值作为初始值。在第 13 行,我们首先给出了一个变量名,之后是短变量声明操作符。这个操作符是冒号加一个等号 (:=)。一个短变量声明操作符在一次操作中完成两件事情:声明一个变量,并初始化。短变量 声明操作符会使用右侧给出的类型信息作为声明变量的类型。
代码清单 5-3 使用结构字面量来声明一个结构类型的变量
// 声明 user 类型的变量,并初始化所有字段
lisa := user{
name: "Lisa",
email: "lisa@email.com",
ext: 123,
privileged: true,
}
当声明结构类型时,字段的类型并 不限制在内置类型,也可以使用其他用户定义的类型,如代码清单 5-6 所示。
代码清单 5-6 使用其他结构类型声明字段
20 // admin 需要一个 user 类型作为管理者,并附加权限
21 type admin struct {
22 person user
23 level string
24 }
代码清单 5-6 展示了一个名为 admin 的新结构类型。这个结构类型有一个名为 person 的 user 类型的字段,还声明了一个名为 level 的 string 字段。当创建具有 person 这种字段 的结构类型的变量时,初始化用的结构字面量会有一些变化,如代码清单 5-7 所示。
代码清单 5-7 使用结构字面量来创建字段的值
26 // 声明 admin 类型的变量
27 fred := admin{
28 person: user{
29 name: "Lisa",
30 email: "lisa@email.com",
31 ext: 123,
32 privileged: true,
33 },
34 level: "super",
35 }
为了初始化 person 字段,我们需要创建一个 user 类型的值。代码清单 5-7 的第 28 行就 是在创建这个值。这行代码使用结构字面量的形式创建了一个 user 类型的值,并赋给了 person 字段。 另一种声明用户定义的类型的方法是,基于一个已有的类型,将其作为新类型的类型说明。 当需要一个可以用已有类型表示的新类型的时候,这种方法会非常好用,如代码清单 5-8 所示。 标准库使用这种声明类型的方法,从内置类型创建出很多更加明确的类型,并赋予更高级的功能
代码清单 5-8 基于 int64 声明一个新类型
type Duration int64
代码清单 5-8 展示的是标准库的 time 包里的一个类型的声明。Duration 是一种描述时间 间隔的类型,单位是纳秒(ns)。这个类型使用内置的 int64 类型作为其表示。在 Duration 类型的声明中,我们把 int64 类型叫作 Duration 的基础类型。不过,虽然 int64 是基础 类型,Go 并不认为 Duration 和 int64 是同一种类型。这两个类型是完全不同的有区别的 类型。 为了更好地展示这种区别,来看一下代码清单 5-9 所示的小程序。这个程序本身无法通过 编译。
代码清单 5-9 给不同类型的变量赋值会产生编译错误
01 package main
02
03 type Duration int64
04
05 func main() {
06 var dur Duration
07 dur = int64(1000)
08 }
代码清单 5-9 所示的程序在第 03 行声明了 Duration 类型。之后在第 06 行声明了一个类型 为 Duration 的变量 dur,并使用零值作为初值。之后,第 7 行的代码会在编译的时候产生编 译错误,如代码清单 5-10 所示。
代码清单 5-10 实际产生的编译错误
prog.go:7: cannot use int64(1000) (type int64) as type Duration in assignment
编译器很清楚这个程序的问题:类型 int64 的值不能作为类型 Duration 的值来用。换句 话说,虽然 int64 类型是基础类型,Duration 类型依然是一个独立的类型。两种不同类型的 值即便互相兼容,也不能互相赋值。编译器不会对不同类型的值做隐式转换。