# 4.4.4-3 基础类型

Go 语言内置的基础类型包括：

* 布尔类型
* 数字类型，例如整数、浮点数、复数等
* 字符串类型

编译器使用同一个结构来表示所有的基础类型，其定义如下：

```go
// A Basic represents a basic type.
type Basic struct {
    kind BasicKind
    info BasicInfo
    name string
}
```

`BasicInfo`用来描述类型属性，例如该类型是否是数字类型，是否可排序。其定义如下：

```go
type BasicInfo int

// Properties of basic types.
const (
    IsBoolean BasicInfo = 1 << iota
    IsInteger
    IsUnsigned
    IsFloat
    IsComplex
    IsString
    IsUntyped

    IsOrdered   = IsInteger | IsFloat | IsString
    IsNumeric   = IsInteger | IsFloat | IsComplex
    IsConstType = IsBoolean | IsNumeric | IsString
)
```

`BasicInfo`用来标识类型的性质，对应的常量申明已经解释了其潜在用途。

`BasicKind`用来标识实际类别，例如`Int32`, `Float64`等；这里简单罗列几个以做示例：

```go
// BasicKind describes the kind of basic type.
type BasicKind int

const (
    Invalid BasicKind = iota // type is invalid

    // predeclared types
    Bool
    // ... 省略其它类型
    Int8
    Complex128
    String
    UnsafePointer

    // types for untyped values
    UntypedBool
    UntypedInt
    UntypedRune
    UntypedFloat
    UntypedComplex
    UntypedString
    UntypedNil

    // aliases
    Byte = Uint8
    Rune = Int32
)
```

可以看出对于每一个基础类型，都有一个枚举类型与之对应，另外可以发现 go 中的`byte`与`rune`类型只是`uint8`与`int32`的类型别名。

这里稍稍多讲一下 Untypedxxx 这一组类型。go 的类型系统非常严格，如果一个表达式的类型是确定的，那么不管在什么场景下，编译器都不会做任何隐式的类型转换，例如如下代码：

```go
var a int16
var b int32

c := a + b // 错误信息：invalid operation: mismatched types int16 and int32
```

在赋值语句`c := a + b` 中，a与b的类型都是确定的整数类型，而将 a 转换为 int32 也是安全的，但 go 并不会这么做。反观 Java 则比较灵活，其定义了一组类型的隐式转换规则，例如如下 Java 代码是合法的：

```java
int a = 10;
float b = 1.0f;
String c = "Java";
String d = a + b + c; 
```

但是 go 也没有死板到无可救药，对于类型还不确定的表达式，go 会根据上下文来进行类型转换。但 go 中仅允许常量在声明时拥有不确定的类型，对应`BasicKind`中`Untypedxxx`的几类。

更准确地说，go 基础类型的字面量都是 untyped 的，例如 "golang", 12, 3.14 分别对应 UntypedString, UntypedInt, UntypedFloat. 当使用这些字面量进行操作，例如赋值、运算、作为参数传递时，编译器会根据上下文将其转换为目标类型。我们来看几个示例：

* 常量声明有类型

```java
const name string = "Golang"
```

"Golang" 类型为 UntypedString, 但是 name 的声明中指定类型为 string, 所以编译器在初始化 name 时会将 "Golang" 转换为 string 类型并且赋值给 name. 如果此时类型转换不匹配，则会报错：

```go
const name string = 1.2 // 错误信息：cannot convert 1.2(untyped float constant) to string
```

* 常量声明无类型

```go
const name = "Golang"
const alias string = name
```

name 没有申明类型，所以其类型复用字面量 "Golang" 的类型 UntypedString; 而 alias 的类型是 string, name 的值赋给 alias 的时候编译器会将其转换为 string 类型

* 根据常量 underlying type 进行转换

```go
type Str string

const name = "Golang"
const alias Str = name

const owner string = "google"
const ownerAlias Str = owner // 错误信息：cannot use owner (constant "google" of type string) as Str value in constant declaration
```

这里我们声明了新的类型 Str, 其 underlying type 是 string, 通过对 alias 的赋值语句可以看出：编译器依然可以将 UntypedString 转换为 Str 类型。但 owner 被显示声明成了 string 类型，其与 Str 毕竟是不同的类型，所以 ownerAlias 的赋值语句中，编译器不会将 string 类型的 owner 隐式转换为 Str 类型。

* 常量声明无类型

```go
type Str string

var name = "Golang"
var alias Str = name // 错误信息：cannot use name (variable of type string) as Str value in variable declaration
```

上面我们提到 go 仅仅支持常量拥有不确定类型，所以即使变量 name 没有申明类型，但编译器会根据右边的值推测出一个具体的类型赋给 name, 这里 "Golang" 的类型是 UntypedString。UntypedString 只能被转换为 string 类型，所以对 alias 的赋值会报错。

Untyped 类型的转换规则很简单：数字类型低精度可以向高精度转换，除此之外只有相同的类型才能相互转换。
