021-类型(Type),类型转换和测试
go 是强类型的语言。
从一开始我们就知道 go 中定义变量是需要指定其类型的,就算是自动推导,那也是需要有类型的,只不过编译器帮你分析了其类型而已。
类型的作用是为了描述 object 相关的特性。比如它规定了 object 占用几个字节,在内存中是怎样的结构,是否支持一些操作符。
比如 go 的内置类型 int32
,它表示整数,在内存中占用 4 个字节(32bit),它支持 +, -, *, /
运算。在现实生活中,有很多东西可以使用 int32
来表示,比如 5 个人,20 岁,50 秒。
尽管 20 岁,50 秒的单位不同,但是他们都可以使用 int32
来描述。有时候,我们需要刻意区分不同单位的数字。比如我们不能用 50秒−20岁
50
秒
−
20
岁
,这完全没有意义。
go 提供了关键字 type,允许我们根据已有的类型创建新类型。它的语法是如下:
type name underlying-type
我们将 underlying-type 称为 name 的底层类型。
比如:
type Second int32
type Year int32
它的语法非常像 c/c++ 里的 typedef
,但是 typedef
的含义是给类型定义新别名。在 go 里,type 关键字不仅仅是给类型定义新别名,而且它添加了更多的特性。比如上面的两种类型 Second
和 Year
,它们之间是不允许直接相互赋值,也不允许互相参与运算。尽管他们都是 int32
的别名类型(以后我们应该说,他们的底层类型都是 int32
)。
go 刻意区分它们的目的是防止你无意犯错,将不同类型的值互相运算。
2. 类型转换再举个例子,我们定义两个类型,一个是 Second
,另一个是 Minute
.
type Second int32 // 秒
type Minute int32 // 分
很显然,Second
和 Minute
是不能直接相互运算的。但是它们之间是可以相互转换的。比如 1 分钟可以换算成 60 秒。
var s Second
var m Minute = 1
s = 60 * m // not ok
在 c/c++ 里,上面是 ok 的,但是在 go 里不行。虽然你给 m
乘以了 60,但是运算结果仍然是 Minute
类型。go 采用的方案是使用显式类型转换:
var s Second
var m Minute = 1
s = Second(60
在 go 里,类型转换的语法就是 T(x)
,T 表示什么任意类型。比如 int32(x)
, (*float64)(x)
这些都是类型转换。不是所有的类型转换都能成功,go 类型转换需要满足下面的条件之一:
[]byte
)之间可以转换。另外,类型转换只发生在编译阶段。这意味着如果编译通过,运行时绝对可以转换成功。
3. 示例这个例子来自 gopl,主要是摄氏温度(Celsius)和华氏温度(Fahrenheit)的互转。代码路径是 gopl/programstructure/type/tempconv
另外,这个例子还演示了 go 如何运行测试用例。
3.1 源码// conv.go
package tempconv
type Celsius float64
type Fahrenheit float64
const (
AbsoluteZeroC Celsius = -273.15
FreezingC Celsius = 0
Boiling Celsius = 100
)
func CToF(c Celsius) Fahrenheit {
f := c*9/5 + 32
return Fahrenheit(f)
}
func FToC(f Fahrenheit) Celsius {
c := (f - 32) * 5 / 9
return
另外,还有一个测试文件 conv_test.go
.
package tempconv
import "fmt"
func ExampleOne() {
c := FToC(32)
fmt.Printf("32F = %.2fC\n", c)
// Output:
// 32F = 0.00C
}
func ExampleTwo() {
c := CToF(5)
fmt.Printf("5C = %.2fF\n", c)
// Output:
// 5C = 41.00F
运行结果:
图1 运行结果
go 另一个强大的功能是天然对测试的支持。
这里我们先简单使用测试的一个功能——运行『示例函数』。就像 3.1 节中 conv_test.go 里的那两个函数。
有一些规则是必须的:
测试文件名必须以_test.go
结尾。测试程序和被测试程序在同一个包里。示例函数名必须以 Example 开头。示例函数必须包含注释// Output:
才会运行(写成// output:
也可以,但是冒号不能丢)// Output:
后面必须要将输出结果写出来满足了上面的条件,就可以运行示例函数了。运行方法是执行 go test -v
,但是你可以不加 -v
选项,加上它只是为了输出更多细节。
练习:
写一个Second
和Minute
互转程序,并写一份测试文件。尝试将 conv_test.go 里的// Output:
后面的输出修改成其它值,比如// Output:
// 32F = 0.00C
修改为
// Output:
// 32F = 0C
再运行 go test -v
看看输出。
版权声明
本文仅代表作者观点,不代表博信信息网立场。