1.变量强类型
变量在声明时,需要明确指定其类型,一旦定义不可改变。
变量,仅仅可以存储对应的类型的数据!
2.静态类型和动态类型
关于变量的类型,其中声明类型称之为静态类型,而真实值类型我们称之为动态类型。
典型的数据类型变量的静态和动态类型保持一致。例如 var x int; x = 42;
// 保持一致 动静一致
var x int // x 的静态类型是
int x = 42 // x 的动态类型是 int
注意:在一种叫做接口的数据类型上,动态类型和静态类型不一致。
优势是 在处理动态数据时,需要在运行时确定数据的真实类型。此时,需要一个机制来处理,所以使用空接口进行处理。
3.空接口,interface{},任意类型
接口,interface,本质用来定义一组操作。
空接口,没有定义任何操作的接口,该接口满足任何类型的定义。
字面量如下: interface{}
空接口的意义在于,可以定义一个变量为空接口类型,使用该变量存储任意的数据数据类型。
代码:
// x 的静态类型是 interface{}
var x interface{}
// 赋值为 42 int 型
x = 42
fmt.Println(x)
// 赋值 "hank" string 型
x = "hank"
fmt.Println(x)
// 赋值 false bool 型
x = false
fmt.Println(x)
其中,x 变量的静态类型 interface{}空接口,而动态类型,随着其赋值而定,分别为 int, string,bool 型。
注意:空接口 interface{} 也是一种数据类型,虽然可以被赋值为 int,string 或其他类型 的数据,但是 interface{} 与其他类型不一致,是一种独立的类型。即使我们将字符串类型 赋值给了空接口类型变量,也不能与字符串类型直接做运算,
代码演示:
var x interface{}
// 赋值 "hank" string 型
x = "hank"
fmt.Println(x + " " + "GoLang")
// 报错:# command-line-arguments
// .\type.go:28:16: invalid operation: x + " GoLang" (mismatched types interface {} and string)
若需要真正的获取空接口变量中存储的特定类型的数据,需要使用类型断言或类型检测 来实现。
4.类型断言
用于断定空接口中的数据是何种类型,断定成功,会获取该类型的数据,断定失败,获
取不成功。 语法如下:
- 数据变量 := 空接口类型变量.(T)
- 数据变量, 断言结果 := 空接口类型变量.(T)
其中 T 是某种特定类型。
空接口类型变量.(Type) 才是断言的语法,支持两种返回形式,直接获取值或者同时获
取断言结果。
代码演示:
var x interface{}
// 赋值 "hank" string 型
x = "hank"
// 断言为字符串类型,不获取断言结果,通常在可以从逻辑上确定数据类型
时使用。
sx := x.(string)
fmt.Printf("%T\n", sx) // string
fmt.Println(sx) // hank
var y interface{}
y = 42
// 断言为字符串类型,需要获取断言结果,通常在不能判断是否为我们需要
的数据类型时使用。提供了错误处理
sy, e := y.(string)
fmt.Println(e) // false
// 当断言失败时,人为处理需要的数据
if !e { // 断言失败
sy = "default string"
}
fmt.Println(sy) // default string
5.类型测试
可以通过 interface.(type) (type 就是 type 关键字,不是数据类型的意思)与 switch 结
构配合使用,完成类型检测。
代码演示:
var x interface{}
x = 42
// x = "hank"
// x = false
switch d := x.(type) {
case int:
fmt.Println(d, d+42) // 42 84
case string:
fmt.Println(d, d + " GoLang") // hank hank Golang
case bool:
fmt.Println(d, !d) // false true
}
注意: 这种语法 和 switch 的常规语法不一致,是一种特殊的语法
6.类型转换
将通过 A 类型的数据,得到 B 类型的数据,该转换过程,称之为类型转换。 语法:
B 类型数据 = B 类型标识(A 类型数据)
代码演示:
// 类型转换
var x int = 42
var y float64 = 42.5
fmt.Println(float64(x) + y) // 84.5
若不转换,语法检测失败,不同类型不能计算:
invalid operation: x + y (mismatched types int and float64)
典型的转换情况:
- 数值类型间的转换,整型转为浮点型,浮点型转换整型
- 字符串和字节切片[]byte相互转换
代码演示:
// 类型转换
var x int = 42
var y float64 = 42.5
fmt.Println(float64(x) + y) // 84.5
fmt.Println(x + int(y)) // 84
var z string = "hank"
fmt.Println([]byte(z)) //[104 97 110 107]
var a []byte = []byte {104, 97, 110, 107,}
fmt.Println(string(a)) // hank
除此之外的类型间的相互转换是不被允许,例如将其他类型转为布尔类型。
代码演示:
var b int = 0
fmt.Println(bool(b))
// 报错: #command-line-arguments
// .\type.go:73:18: cannot convert b (type int) to type bool
7.类型定义
自定义的的数据类型。 可以基于内置的数据类型,构建自定义的数据类型,称之为类型定义。使用语法: type 类型标识符 类型描述字面量
最典型的情况,就是结构体类型的定义:
type Person struct {
ID string
Name string
age int
}
其中,Person 就是我们自定义的数据类型,其对应的类型描述,是后边的结构体类型 的字面量。
除了结构体,我们可以使用任意的内置类型来定义我们的自定义类型,演示:
// 类型定义
type age int
type height int
type position struct {
x int
y int
}
type hobby []string
定义好的数据类型,使用的语法与内置的类型一致,演示:
type age int
var a age = 42
fmt.Println(a, a + 42) // 42 84
type hobby []string
var c hobby = hobby{"basketball", "football"}
//亦可 :c := hobby{"basketball", "football"}
fmt.Println(c, len(c), c[1]) // [basketball football] 2 football
注意:基于某种类型定义的新类型,继承某种类型的方法集合(操作集合),age 就支持 int
型加减乘除运算,hobby 类型就支持 []string 的索引,长度等运算。但是,定义的类型与其基础类型(就是 age 和 int)是不同的两种数据类型,不能做直接运算,但是可以通过类型转换,转换为对应的类型。
var a age = 42
var b int = 1024
// fmt.Println(a, a + 42, a + b)
// 报错 .\type.go:86:27: invalid operation: a + b (mismatched types age and int)
fmt.Println(a, a + 42, a + age(b), int(a) + b)
// 输出:42 84 1066 1066
为了统一概念,任何数据类型都有基础类型,包括 int 和[]string,这些预定义的内置类 型或类型的字面量的基础类型是其本身。
- age 和 int 的基础类型都是 int
- hobby he []string 的基础类型都是 []string
其中,类型字面量包括自定义类型的字面量类型,例如,若:type ages []age,其中 ages 的基础类型就是 []age。
8.类型别名
别名,另一个名字,同一个类型的不同的名字,称之为类型别名。
语法:type 别名 = 类型 (与类型定义的差异是存在等号)
代码演示:
type age = int
var a age = 42
var b int = 1024
fmt.Println(a, a + 42, a + b) // 42 84 1066
给类型取一个满足业务逻辑的名字,并没有增加新类型,因此不同别名但同一类型间的
数据,可以直接做运算!
内置的类型:
byte 和 rune 其实就是通过别名的方案来定义的,也是整型集团内的。
// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8
// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32
9.其他功能类型
功能上的类型,不表示数据,用于实现某个特定功能。有:
- 函数,function,定义功能模块
- 接口,interface,用于定义操作限定
- 信道,channel,完成goroutine间的通讯
网友评论