数值类型
整数类型
Kotlin 提供了一组内建数据类型来表达数值. 对于整数数值, 有 4 种数据类型, 它们的大小不同, 因此表达的数值范围也不同:
类型 | 大小(bits) | 最小值 | 最大值 |
---|---|---|---|
| 8 | -128 | 127 |
| 16 | -32768 | 32767 |
| 32 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
| 64 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
如果你初始化一个变量, 不明确指定类型, 编译器会自动推断类型, 使用从 Int
开始、足够表达这个值的最小的整数范围. 如果值没有超过 Int
类型的最大范围, 那么类型会推断为 Int
. 如果超过, 那么类型将是 Long
. 如果要明确指明一个数值是 Long
类型, 请在数值末尾添加 L
后缀. 如果明确指定类型, 编译器会检查值有没有超过指定类型的最大范围.
浮点类型
对于实数数值, Kotlin 提供了符合 IEEE 754 标准 的浮点类型 Float
和 Double
. Float
代表 IEEE 754 单精度(single precision)浮点数, 而 Double
代表 双精度(double precision)浮点数.
这两种类型的区别在于它们大小, 以及能够存储的浮点数值精度:
类型 | 大小(bits) | 有效位数 | 指数位数 | 十进制位数 |
---|---|---|---|---|
| 32 | 24 | 8 | 6-7 |
| 64 | 53 | 11 | 15-16 |
可以使用带小数部分的数值初始化 Double
和 Float
变量. 小数部分与整数部分用点号(.
)分隔. 任何变量如果使用浮点数值初始化, 编译器推断的类型将是 Double
:
如果要明确指明一个数值是 Float
类型, 请在数值末尾添加 f
或 F
后缀. 如果这个值包含 6-7 位以上的十进制数字, 这部分会被舍去:
与其他一些语言不同, Kotlin 的数值类型没有隐式的拓宽变换. 比如, 如果函数使用 Double
参数, 那么只能使用 Double
值调用它, 而不能使用 Float
, Int
, 或其他数值类型的值:
如果要将数值转换为不同的类型, 请使用 显式类型转换.
数值的字面值常数(Literal Constant)
对于整数值, 有以下几种类型的字面值常数:
10进制数:
123
Long 类型需要大写的
L
来标识:123L
16进制数:
0x0F
2进制数:
0b00001011
Kotlin 还支持传统的浮点数值表达方式:
无标识时默认为 Double 值:
123.5
,123.5e10
Float 值需要用
f
或F
标识:123.5f
你可以在数字字面值中使用下划线, 提高可读性:
数值类型在 JVM 平台的内部表达
在 JVM 平台中, 数值的存储使用基本类型: int
, double
, 等等. 除非你创建一个可为 null 的数值引用, 比如 Int?
, 或使用泛型. 这种情况下数值会被装箱(box)为 Java 类 Integer
, Double
, 等等.
可为 null 的数值引用即使指向相同的数值, 也可能指向不同的对象:
所有指向 a
的可为 null 的引用实际上都是同一个对象, 因为JVM 针对 -128
与 127
之间的 Integer
类型会进行内存优化. 但这种优化对 b
的引用无效, 因此这些引用是不同的对象.
但是, 对象仍然是相等的:
显式数值类型转换
由于数据类型内部表达方式的差异, 较小的数据类型 不是较大数据类型的子类型(subtype). 如果小数据类型是大数据类型的子类型, 那么我们将会遇到以下问题:
这样, 不仅不能保持同一性(identity), 而且还静悄悄地失去了内容相等性(equality).
由于存在以上问题, Kotlin 中较小的数据类型 不会隐式地转换为 较大的数据类型. 也就是说, 要将一个 Byte
类型值赋给一个 Int
类型的变量需要进行显式类型转换:
所有的数值类型都可以转换为其他类型:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
大多数情况下并不需要明确的的类型转换, 因为类型可以通过代码上下文自动推断得到, 而且数学运算符都进行了重载(overload), 可以适应各种数值类型的参数, 比如:
数值类型的运算符(Operation)
Kotlin 对数值类型支持标准的数学运算符(operation): +
, -
, *
, /
, %
. 这些运算符定义为相应的数值类上的成员函数:
你也可以对自己的类覆盖这些运算符. 详情请参见 操作符重载(Operator overloading).
整数除法
整数值之间的除法返回的永远是整数值. 所有的小数部分都会被抛弃.
对任何两种整数类型之间的除法都是如此:
如果要返回浮点类型的结果, 需要将其中一个操作数显式转换为浮点类型:
位运算符
Kotlin 对整数值提供了一组 位运算符. 这些运算符直接对数值的二进制表达的位(bit)进行操作. 位运算符表达为函数, 可以通过中缀表示法调用. 只能用于 Int
和 Long
:
以下是位运算符的完整列表:
shl(bits)
– 带符号左移shr(bits)
– 带符号右移ushr(bits)
– 无符号右移and(bits)
– 按位与(AND)or(bits)
– 按位或(OR)xor(bits)
– 按位异或(XOR)inv()
– 按位取反
浮点值的比较
本节我们讨论的浮点值操作包括:
相等判断:
a == b
以及a != b
比较操作符:
a < b
,a > b
,a <= b
,a >= b
浮点值范围(Range) 的创建, 以及范围检查:
a..b
,x in a..b
,x !in a..b
如果操作数 a
和 b
的类型能够静态地判定为 Float
或 Double
(或者可为 null 值的 Float?
或 Double?
), (比如, 类型明确声明为浮点值, 或者由编译器推断为浮点值, 或者通过智能类型转换变为浮点值), 那么此时对这些数值, 或由这些数值构成的范围的操作, 将遵循 IEEE 754 浮点数值运算标准.
但是, 为了支持使用泛型的情况, 并且支持完整的排序功能, 如果操作数 不能 静态地判定为浮点值类型, 那么判定结果会不同. 例如, Any
, Comparable<...>
, 或 Collection<T>
类型. 对于这样的情况, 对这些浮点值的操作将使用 Float
和 Double
类中实现的 equals
和 compareTo
方法. 因此判定结果是:
NaN
会被判定为等于它自己NaN
会被判定为大于任何其他数值, 包括正无穷大(POSITIVE_INFINITY
)-0.0
会被判定为小于0.0
下面是一段的示例程序, 演示静态地判定为浮点值类型的操作数(Double.NaN
) 与 不能 静态地判定为浮点值类型的操作数 (listOf(T)
) 之间的动作差别.