1. 概述
在处理数值时,我们经常会遇到需要判断一个数是正数还是负数的场景。
本文将探讨如何在 Kotlin 中高效地判断一个数字的正负性。我们将逐步构建一个符合 Kotlin 风格的通用函数,能够对任意 Number
类型的实例进行判断。
目标很明确:✅ 实现一个能识别 Int
、Long
、Float
、Double
等所有 Number
子类型,并返回其分类(正、负、零)的函数。
2. 问题引入
判断一个整数的正负很简单,但 Kotlin 的数值类型多样——Int
、Long
、Float
、Double
等都继承自抽象类 Number
。如果我们为每种类型都写一个判断函数,那代码就太冗余了 ❌。
我们的目标是:编写一个统一的、idiomatic 的 Kotlin 函数,能处理所有 Number
类型。
为了更清晰地表达结果,我们先定义一个枚举类:
enum class NumberCategory {
Positive, Negative, Zero
}
接下来,我们从最简单的 Int
开始,逐步演进到通用方案。
3. 使用 if-else 表达式
最直接的方式就是与 0 比较。Kotlin 的 if-else
是表达式,可以直接返回值:
fun categoryOfInt1(n: Int): NumberCategory {
return if (n > 0) {
Positive
} else if (n < 0) {
Negative
} else {
Zero
}
}
测试验证一下:
assertEquals(Positive, categoryOfInt1(42))
assertEquals(Negative, categoryOfInt1(-42))
assertEquals(Zero, categoryOfInt1(0))
测试通过 ✅。逻辑清晰,但略显啰嗦,可读性还有提升空间。
4. 使用 when 表达式优化
Kotlin 的 when
表达式在多分支判断时比 if-else
更简洁、更具可读性 ✅。
改写如下:
fun categoryOfInt2(n: Int): NumberCategory {
return when {
n > 0 -> Positive
n < 0 -> Negative
else -> Zero
}
}
结构一目了然,测试同样通过:
assertEquals(Positive, categoryOfInt2(42))
assertEquals(Negative, categoryOfInt2(-42))
assertEquals(Zero, categoryOfInt2(0))
when
的无参形式在这里用得恰到好处,避免了冗余的条件判断。
5. 扩展至所有 Number 类型
上面的函数只支持 Int
。如果要支持 Long
、Float
等,难道要写一堆重载?显然不是 Kotlin 的风格 ❌。
核心思路
Number
类提供了 toDouble()
、toInt()
等转换方法。我们可以将任意 Number
转为 Double
进行比较。
⚠️ 为什么选 Double
?
Double
能表示的数值范围最大- 整型转
Double
不会丢失精度(在合理范围内) - 浮点型本身就是
Double
或可无损转为Double
实现如下:
fun categoryOfNum(n: Number): NumberCategory {
val d = n.toDouble()
return when {
d > 0.0 -> Positive
d < 0.0 -> Negative
else -> Zero
}
}
全类型测试覆盖
// Int
assertEquals(Positive, categoryOfNum(42))
assertEquals(Negative, categoryOfNum(-42))
assertEquals(Zero, categoryOfNum(0))
// Long
assertEquals(Positive, categoryOfNum(42L))
assertEquals(Negative, categoryOfNum(-42L))
assertEquals(Zero, categoryOfNum(0L))
// Double
assertEquals(Positive, categoryOfNum(42.42))
assertEquals(Negative, categoryOfNum(-42.42))
assertEquals(Zero, categoryOfNum(0.00))
// Float
assertEquals(Positive, categoryOfNum(42.42f))
assertEquals(Negative, categoryOfNum(-42.42f))
assertEquals(Zero, categoryOfNum(0.00f))
全部通过 ✅。一个函数搞定所有数值类型,这才是我们要的通用解。
6. 进阶优化:使用扩展函数提升 API 友好性
虽然 categoryOfNum()
功能完整,但调用方式不够自然:categoryOfNum(42)
总感觉像在“外部加工”数据。
Kotlin 的扩展函数可以让我们把函数“挂”在 Number
上,调用更流畅:
fun Number.category(): NumberCategory {
val d = toDouble()
return when {
d > 0.0 -> Positive
d < 0.0 -> Negative
else -> Zero
}
}
现在调用变得非常自然:
// Int
assertEquals(Positive, 42.category())
assertEquals(Negative, (-42).category())
assertEquals(Zero, 0.category())
// Float
assertEquals(Positive, 42.42f.category())
assertEquals(Negative, (-42.42f).category())
assertEquals(Zero, 0.00f.category())
这种写法更符合 Kotlin 的惯用法(idiomatic),API 也更贴近开发者直觉 ✅。
7. 总结
我们从一个简单的 if-else
开始,逐步演化出一个通用、简洁、符合 Kotlin 风格的数值正负判断方案:
- 基础判断:用
when
替代if-else
,提升可读性 - 类型统一:利用
Number.toDouble()
实现多类型支持 - API 优化:通过扩展函数让调用更自然流畅
最终的 Number.category()
扩展函数既实用又优雅,可以直接集成到项目中。
示例完整代码已上传至 GitHub:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-numbers-2