Go语言底层原理剖析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

4.2 常量类型转换

如下所示,常量可以进行类型推断。第3章详细介绍了未定义的常量是如何进行转换的,在转换为具体的类型之前,Go语言编译时会使用一种高精度的结构存储常量。

本节还将介绍常量的其他转换规则。

4.2.1 隐式整数转换

在Go语言中,变量之间没有隐式类型转换,不同的类型之间只能强制转换。但是,编译器可以进行变量和常量之间的隐式类型转换。

如下所示,将整数常量123隐式转换为int。由于常量不使用小数点或指数,因此采用的类型为整数。只要不需要截断,就可以将类型为整数的未命名常量隐式转换为有符号和无符号命名常量。

如果常量使用与整数兼容的类型,也可以将浮点常量隐式转换为整数变量:

但是下面的转换是不可行的,无法对常量进行截断。

4.2.2 隐式浮点数转换

如下所示,编译器可以将类型为小数的未命名常量0.333隐式转换为单精度或双精度类型的浮点数。

另外,编译器可以在整数常量与float64变量之间进行隐式转换。

4.2.3 常量运算中的隐式转换

常量与变量之间的运算在程序中最常见,它遵循Go语言规范中运算符的规则。该规则规定,除非操作涉及位运算或未定义的常量,否则操作数两边的类型必须相同。这意味着常量在进行运算时,操作数两边的类型不一定相同,如下所示。

在Go语言规范中,对于常量表达式也制定了专门的规则。除了移位操作,如果操作数两边是不同类型的无类型常量,则结果类型的优先级为:整数(int)<符文数(rune)<浮点数(float)<复数(Imag)。根据此规则,上面两个常数之间相乘的结果将是一个浮点数,因为浮点数的优先级比整数高。下面的例子结果为浮点数。

下面的例子将在整数常量之间进行除法运算,结果必然是一个整数常量。由于3除1的值小于1,因此该除法的结果为整数常量。

4.2.4 常量与变量之间的转换

常量与具体类型的变量之间的运算,会使用已有的具体类型。例如下例中常量p为float64,常量2会转换为和b相同的类型。

下面的例子会报错,因为2.3不能转换为b的类型int。

4.2.5 自定义类型的转换

当常量转换涉及用户自定义的类型时,会变得更加复杂。下例中声明了一个新类型,称为Numbers,其基本类型为int8。接着以Numbers声明常量One,并分配整数类型的常量1。最后声明常量Two,常量Two通过未命名常量2和Numbers类型的常量One相乘转换为了Numbers类型。

自定义类型在实际开发和Go源码中都很常见,例如在标准库time中,声明时间常量的方式使用了自定义类型的转换。

由于编译器将对常量执行隐式转换,因此可以在Go语言中像下面一样编写代码[3]

Add方法接受一个类型为Duration的参数,Time包中的Add方法的定义如下。

在上例实际调用Add函数的过程中,将-5与-fiveSeconds作为函数也并没有出错,这是由于编译器会将常量-5隐式转换为类型为Duration的变量,以允许方法调用。同时,基于常量的运算规则可知,常量FiveSeconds的类型为Duration。

但是,如果指定了变量的类型,则调用不会成功。例如,在下面的例子中,一旦我们使用具体类型的整数值作为Add方法调用的参数,就会收到编译器报错。编译器不允许在类型变量之间进行隐式类型转换。

报错为

为了编译该代码,需要在程序中执行显式的类型转换。