数值类型 Kotlin 的数值类型表示:
使用数值类型来存储和处理数值数据, 例如算术运算, 计数器, 测量值, 以及其他计算.
选择数值类型 在大多数情况下, 可以参考以下规则, 为你的任务确定正确的数值类型:
整数类型 Kotlin 提供了 4 种整数类型, 有着不同的大小和数值范围:
类型
大小(bits)
最小值
最大值
Byte
8
-128
127
Short
16
-32768
32767
Int
32
-2,147,483,648 (-231 )
2,147,483,647 (231 - 1)
Long
64
-9,223,372,036,854,775,808 (-263 )
9,223,372,036,854,775,807 (263 - 1)
声明整数值 Kotlin 对整数值支持以下字面值(literal)形式:
10 进制数: 123
16 进制数: 0x0F
2 进制数: 0b00001011
要声明一个数值, 请明确指定类型:
val one: Int = 1
// 使用下划线提高可读性
val oneBillion: Long = 1_000_000_000
val hexBytes: Int = 0x7F_EC_DE_5E
val bytes: Int = 0b01010010_01101001_10010100_10010010
val oneByte: Byte = 1
val oneShort: Short = 1
也可以添加 L 后缀, 声明一个 Long 类型的值:
val oneLong = 1L
当你明确声明数值类型时, 编译器会检查值是否在该类型的范围内:
// 值在 Byte 范围内
val oneByte: Byte = 1
// 错误: 值不在 Byte 范围内
val tooBig: Byte = 128
当你没有指定数值类型时, 如果值在 Int 范围内, Kotlin 推断类型为 Int. 否则推断类型为 Long:
val million = 1_000_000 // Int 类型
val threeBillion = 3_000_000_000 // Long 类型
如果值可以为空, 请使用可为 null 的类型:
val maybeAbsent: Int? = null
浮点类型 对于带小数部分的数值, Kotlin 提供了 Float 和 Double 类型.
浮点类型遵循 IEEE 754 标准 . Float 代表 单精度(Single Precision) . Double 代表 双精度(Double Precision) .
浮点类型的大小和精度有所不同:
类型
大小(bits)
有效位数
指数位数
十进制位数
Float
32
24
8
6-7
Double
64
53
11
15-16
声明浮点值 要声明浮点数字面值, 请包含小数点 (.), 或使用指数表示法:
val pi = 3.14
val avogadro = 6.02214076e23
默认情况下, Kotlin 将浮点数字面值推断为 Double. 要声明 Float, 请添加 f 或 F 后缀:
val pi = 3.14 // Double 类型
val eFloat = 2.7182817f // Float 类型
如果 Float 字面值包含的精度超过 Float 所能存储的范围, Kotlin 会对其进行舍入.
如果值可以为空, 请使用可为 null 的类型:
val maybeAbsent: Double? = null
算术运算 Kotlin 对数值支持标准的算术运算: +, -, *, /, 以及 %.
使用这些运算符来进行常见的计算:
fun main() {
//sampleStart
println(1 + 2)
// 输出结果为: 3
println(2_500_000_000L - 1L)
// 输出结果为: 2499999999
println(3.14 * 2.71)
// 输出结果为: 8.5094
println(10.0 / 3)
// 输出结果为: 3.3333333333333335
//sampleEnd
}
结果类型取决于操作数的类型. 详情请参见 混合数值表达式 .
整数除法 整数值之间的除法返回的永远是整数结果. 编译器会舍弃小数部分:
fun main() {
//sampleStart
val intValue = 5 / 2
println(intValue)
// 输出结果为: 2
val longValue = 5L / 2
println(longValue)
// 输出结果为: 2
//sampleEnd
}
要返回浮点结果, 请将至少一个操作数转换为 Float 或 Double:
fun main() {
//sampleStart
val a = 5 / 2.0
println(a)
// 输出结果为: 2.5
val b = 5 / 2.toDouble()
println(b)
// 输出结果为: 2.5
//sampleEnd
}
类型转换 数值类型互相之间不是子类型(subtype). Kotlin 要求明确的转换, 以免在不知情的情况下丢失数据, 或发生预想之外的行为.
例如, 期望 Double 类型的函数, 如果不进行转换, 不能接受 Int 或 Float 值:
fun main() {
//sampleStart
fun printDouble(x: Double) {
print(x)
}
val x = 1.0
val xInt = 1
val xFloat = 1.0f
val one: Double = 1 // 错误: 初始值类型不匹配
printDouble(x) // OK
printDouble(xInt) // 错误: 参数类型不匹配
printDouble(xFloat) // 错误: 参数类型不匹配
//sampleEnd
}
所有的数值类型都支持转换为其他数值类型. 要将数值转换为其他类型, 请使用明确转换的函数:
toByte()
toShort()
toInt()
toLong()
toFloat()
toDouble()
例如, 以下代码将 Int 值转换为 Double:
fun main() {
//sampleStart
val intValue: Int = 1
val doubleValue = intValue.toDouble()
println(doubleValue)
// 输出结果为: 1.0
//sampleEnd
}
将浮点值转换为整数类型时, 编译器会舍弃小数部分:
fun main() {
//sampleStart
val d: Double = 1.5
val l: Long = d.toLong()
println(l)
// 输出结果为: 1
//sampleEnd
}
混合数值表达式 Kotlin 不支持对赋值语句或函数参数进行隐式类型转换. 但是, 你可以在算术表达式中组合不同的数值类型. 在这种情况下, Kotlin 根据操作数类型确定结果类型, 算术运算符会自动处理转换:
val intNumber: Int = 1
val longNumber: Long = 1000
val result = intNumber + longNumber // 结果为: 1001, Long 类型
如果你尝试将结果赋给较小的类型, 编译器会报告错误:
val intNumber: Int = 1
val longNumber: Long = 1000
val result: Int = intNumber + longNumber
// 错误: 初始值类型不匹配
整数字面值类型 在类型推断过程中, Kotlin 将没有后缀的整数字面值当作一种特殊的 整数字面值类型(Integer Literal Type, ILT) , 直到周围的上下文确定具体的类型为止:
//sampleStart
fun List<Any>.log() {
println(joinToString(" | ") { it::class.simpleName ?: "Unknown" })
}
fun main() {
listOf(1, 2).log()
// 输出结果为: Int | Int
listOf(1L, 2L).log()
// 输出结果为: Long | Long
// 编译器将 1 解释为 ILT, 并解析为 Long 类型
listOf(1, 2L).log()
// 输出结果为: Long | Long
// .toInt() 将字面值转换为 Int
listOf(1.toInt(), 2L).log()
// 输出结果为: Int | Long
}
//sampleEnd
对于 Int 和 Long 值, 这一点尤其容易被忽略, 因为在运行期它们的字符串表达是相同的. 为了避免这个问题, 请指定期望的类型, 或者明确的转换这些值:
//sampleStart
fun List<Any>.log() {
println(joinToString(" | ") { it::class.simpleName ?: "Unknown" })
}
fun main() {
val longValues: List<Long> = listOf(1, 2L)
longValues.log()
// 输出结果为: Long | Long
val numberValues: List<Number> = listOf(1.toInt(), 2L)
numberValues.log()
// 输出结果为: Int | Long
}
//sampleEnd
你也可以使用明确的类型, 来捕获预期之外的类型推断:
fun main() {
//sampleStart
val intValues: List<Int> = listOf(1, 2L)
// 这里发生错误: initializer type mismatch
//sampleEnd
}
数据溢出 数值类型只能表示其定义范围内的值.
如果运算结果超出该范围, 就会发生溢出. 如果将值转换为较小的数值类型, 转换后的值可能无法保留原来的数值.
即使编译器允许这样的代码, 这种行为也可能影响你的代码的运行结果.
运算中的溢出 每种整数类型只能存储其定义范围内的值. 当算术运算的结果超过该范围时, 就会发生_数据溢出_:
fun main(){
//sampleStart
val intNumber: Int = 2147483647
// Int 的最大值是 2147483647
println(intNumber + 1)
// 输出结果为: -2147483648
//sampleEnd
}
这里, 结果发生了回绕(wrap around), 因为值超过了 Int 的表达范围.
取反中的溢出 取反操作也可能发生溢出. 例如, Int.MIN_VALUE 的正数对应值, 无法用 Int 表示.
fun main(){
//sampleStart
val min = Int.MIN_VALUE
println(-min)
// 输出结果为: -2147483648
//sampleEnd
}
窄化转换 当你将值转换为较小的整数类型时, 结果可能无法保留原来的数值:
fun main() {
//sampleStart
val large: Int = 130
val narrowed: Byte = large.toByte()
println(narrowed)
// 输出结果为: -126
//sampleEnd
}
但是, 由于浮点类型遵循 IEEE 754 标准 , 非常大的结果可能变为 Infinity:
fun main() {
//sampleStart
println(Double.MAX_VALUE * 2)
// 输出结果为: Infinity
//sampleEnd
}
位运算 Kotlin 为 Int 和 Long 提供了 位运算 . 这些运算由一组 中缀函数 和 inv() 表示.
fun main() {
//sampleStart
val x = 1
println(x shl 2)
// 输出结果为: 4
println(x and 0x000FF000)
// 输出结果为: 0
//sampleEnd
}
位运算包括:
shl() – 带符号左移
shr() – 带符号右移
ushr() – 无符号右移
and() – 按位与(AND)
or() – 按位或(OR)
xor() – 按位异或(XOR)
inv() – 按位取反
浮点值的比较 在 Kotlin 中, 浮点数的比较取决于操作数的静态类型.
当操作数静态的判定为 Float 或 Double 类型时, 对这些数值的操作以及由它们构成的范围, 将遵循 IEEE 754 浮点数值运算标准 .
但是, 在使用泛型的情况下 (例如 Any, Comparable<...>, 或 Collection<T>), 对于没有静态的判定为浮点值类型的操作数, 行为有所不同. 在这种情况下, Kotlin 使用 Float 和 Double 的 equals() 和 compareTo() 实现.
因此判定结果是:
以下示例演示静态的判定为浮点值类型的操作数, 与通过泛型类型使用的操作数之间的差别:
//sampleStart
fun generalizedEquals(a: Any, b: Any): Boolean {
return a == b
}
fun main() {
// 操作数静态的判定为浮点值类型
println(Double.NaN == Double.NaN)
// 输出结果为: false
println(0.0 == -0.0)
// 输出结果为: true
// 操作数通过非浮点静态类型使用
println(generalizedEquals(Double.NaN, Double.NaN))
// 输出结果为: true
println(generalizedEquals(0.0, -0.0))
// 输出结果为: false
}
//sampleEnd
JVM 上数值的装箱(Box)和缓存 在 JVM 平台, 非 null 的数值通常使用基本类型存储, 例如 int, long, 或 double. 但是, 当你使用 泛型 , 或可为 null 的数值类型(例如 Int?) 时, 值会被装箱(box), 并以对象的形式表示.
JVM 通过缓存小数值的装箱表示, 使用一种 内存优化技术 . 因此, 具有相同值的装箱数值, 可以是 引用相等的 .
例如, JVM 缓存了 -128 到 127 范围内的装箱 Integer 值. 因此, 以下代码返回 true:
fun main() {
//sampleStart
val score: Int = 100
val savedScore: Int? = score
val displayedScore: Int? = score
println(savedScore === displayedScore)
// 输出结果为: true
//sampleEnd
}
对于缓存范围之外的值, 装箱值是不同的对象. 在这种情况下, 即使它们的值 结构相等 , 它们也不是引用相等的. 因此, 请使用 == 来比较数值:
fun main() {
//sampleStart
val score: Int = 10000
val savedScore: Int? = score
val displayedScore: Int? = score
println(savedScore === displayedScore)
// 输出结果为: false
println(savedScore == displayedScore)
// 输出结果为: true
//sampleEnd
}
2026/09/29