Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

数字类型

不同类型的数字以不同的方式存储。

如果你创建一个标识符并给它赋一个整数值,Kotlin 会推断它的类型为 Int

// NumberTypes/InferInt.kt

fun main() {
  val million = 1_000_000  // 推断为 Int
  println(million)
}
/* 输出:
1000000
*/

为了提高可读性,在数字值中,Kotlin 允许在数字之间使用下划线。

基本的数学运算符与大多数编程语言中可用的一样:加法(+)、减法(-)、除法(/)、乘法(*)和模运算(%),它会产生整数除法的余数:

// NumberTypes/Modulus.kt

fun main() {
  val numerator: Int = 19
  val denominator: Int = 10
  println(numerator % denominator)
}
/* 输出:
9
*/

整数除法会截断其结果:

// NumberTypes/IntDivisionTruncates.kt

fun main() {
  val numerator: Int = 19
  val denominator: Int = 10
  println(numerator / denominator)
}
/* 输出:
1
*/

如果运算会对结果四舍五入,输出会是 2

运算的优先级遵循基本算术规则:

// NumberTypes/OpOrder.kt

fun main() {
  println(45 + 5 * 6)
}
/* 输出:
75
*/

乘法操作 5 * 6 首先进行,然后是加法 45 + 30

如果你想先进行 45 + 5,可以使用括号:

// NumberTypes/OpOrderParens.kt

fun main() {
  println((45 + 5) * 6)
}
/* 输出:
300
*/

现在让我们来计算 体重指数(BMI),它是以千克为单位的体重除以身高的平方。如果你的 BMI 小于 18.5,你体重过轻。在 18.524.9 之间是正常体重。BMI 为 25 及以上则是超重。此示例还展示了在函数的参数不能放在一行上时的首选格式化风格:

// NumberTypes/BMIMetric.kt

fun bmiMetric(
  weight: Double,
  height: Double
): String {
  val bmi = weight / (height * height)  // [1]
  return if (bmi < 18.5) "体重过轻"
    else if (bmi < 25) "正常体重"
    else "超重"
}

fun main() {
  val weight = 72.57 // 160 磅
  val height = 1.727 // 68 英寸
  val status = bmiMetric(weight, height)
  println(status)
}
/* 输出:
正常体重
*/
  • [1] 如果你移除了括号,你会先将 weight 除以 height,然后再将该结果乘以 height。这会得到一个更大的数字,但是答案是错误的。

bmiMetric() 使用 Double 类型来表示体重和身高。Double 可以存储非常大和非常小的浮点数。

下面是使用英制单位的版本,使用 Int 参数表示:

// NumberTypes/BMIEnglish.kt

fun bmiEnglish(
  weight: Int,
  height: Int
): String {
  val bmi =
    weight / (height * height) * 703.07 // [1]
  return if (bmi < 18.5) "体重过轻"
    else if (bmi < 25) "正常体重"
    else "超重"
}

fun main() {
  val weight = 160
  val height = 68
  val status = bmiEnglish(weight, height)
  println(status)
}
/* 输出:
体重过轻
*/

为什么结果与使用 DoublebmiMetric() 不同?当你将一个整数除以另一个整数时,Kotlin 会生成一个整数结果。在整数除法中处理余数的标准方式是 截断,也就是“截掉并抛弃”(没有四舍五入)。所以,如果你将 5 除以 2,会得到 27/10 会得到 0。当 Kotlin 在表达式 [1] 中计算 bmi 时,它将 160 除以 68 * 68 得到 0。然后它将 0 乘以 703.07 得到 0

为了避免这个问题,将 703.07 移到计算的开头。这样会强制计算为 Double 类型:

val bmi = 703.07 * weight / (height * height)

bmiMetric() 中使用的 Double 参数可以防止这个问题。尽早将计算转换为所需类型,以保持准确性。

所有编程语言都有一个限制,不能存储超过整数范围的值。Kotlin 的 Int 类型

可以在 -231 到 +231-1 之间取值,这是 Int 32 位表示的限制。如果你对两个足够大的 Int 进行求和或乘法,你将会溢出结果:

// NumberTypes/IntegerOverflow.kt

fun main() {
  val i: Int = Int.MAX_VALUE
  println(i + i)
}
/* 输出:
-2
*/

Int.MAX_VALUE 是一个预定义的值,表示 Int 可以持有的最大值。

溢出会产生一个明显错误的结果,因为它既是负数,又比我们预期的要小得多。当 Kotlin 检测到潜在溢出时,会发出警告。

防止溢出是你作为开发者的责任。Kotlin 并不能在编译时总是检测到溢出,也不会阻止溢出,因为那会产生不可接受的性能影响。

如果你的程序包含大数字,你可以使用 Long,它可以容纳从 -263 到 +263-1 的值。要定义一个 Long 类型的 val,你可以显式地指定类型,或者在数字字面值末尾加上 L,告诉 Kotlin 将该值视为 Long

// NumberTypes/LongConstants.kt

fun main() {
  val i = 0          // 推断为 Int
  val l1 = 0L        // L 创建 Long
  val l2: Long = 0   // 显式类型
  println("$l1 $l2")
}
/* 输出:
0 0
*/

通过使用 Long,我们防止了 IntegerOverflow.kt 中的溢出问题:

// NumberTypes/UsingLongs.kt

fun main() {
  val i = Int.MAX_VALUE
  println(0L + i + i)              // [1]
  println(1_000_000 * 1_000_000L)  // [2]
}
/* 输出:
4294967294
1000000000000
*/

[1][2] 中使用数值字面值会强制进行 Long 计算,也会产生 Long 类型的结果。L 出现的位置并不重要。如果其中一个值是 Long,则结果表达式也是 Long

尽管它们可以容纳比 Int 大得多的值,但 Long 仍然有大小限制:

// NumberTypes/BiggestLong.kt

fun main() {
  println(Long.MAX_VALUE)
}
/* 输出:
9223372036854775807
*/

Long.MAX_VALUELong 可以容纳的最大值。

练习和解答可在 www.AtomicKotlin.com 找到。