1. 理解问题

在 Kotlin 中,大于和小于的比较是编程中最基础的操作之一。通常,我们这样写:

val int1 = 1
val int2 = 2

val isInt1GreaterThanInt2 = int1 > int2
val isInt1LessThanInt2 = int1 < int2

这些操作符在底层调用的是 compareTo() 方法。该方法返回一个整数表示比较结果:

  • 正数表示第一个值大于第二个
  • 零表示两个值相等
  • 负数表示第一个值小于第二个

⚠️ 但问题在于:如果比较的整数是可空类型(Int?),直接使用这些操作符会导致编译错误

因此,我们需要一种空安全的比较方式来处理可空整数。

2. 使用 compareTo() 方法

Kotlin 提供了一个简单的方法来比较可空整数,即使用 compareTo() 并结合安全调用操作符 ?.

fun compareTwoInts(a: Int?, b: Int?): Int? {
    return a?.compareTo(b ?: return null)
}

这段代码做了以下几件事:

  • 使用 a?.compareTo(...) 避免 a 为 null 时调用方法
  • 如果 b 为 null,则使用 Elvis 操作符(?:)返回 null

✅ 这种方式简洁有效,适合直接比较两个可空整数。

我们来写个测试验证一下:

@Test
fun `compare nullable integers using compareTo`() {
    val a: Int? = 5
    val b: Int? = 3
    val c: Int? = null
    assertEquals(0, compareTwoInts(a, a))
    assertEquals(1, compareTwoInts(a, b))
    assertEquals(-1, compareTwoInts(b, a))
    assertEquals(null, compareTwoInts(a, c))
    assertEquals(null, compareTwoInts(c, b))
}

3. 创建扩展函数

为了复用比较逻辑,我们可以为 Int? 类型创建一个扩展函数:

fun Int?.isGreaterThan(other: Int?): Boolean? {
    return this?.let {
        it > (other ?: return null)
    }
}

✅ 这样封装后,主逻辑更清晰,空值处理也统一了。

测试代码如下:

@Test
fun `compare nullable integers using extension function`() {
    val a: Int? = 5
    val b: Int? = 3
    val c: Int? = null

    assertEquals(true, a.isGreaterThan(b))
    assertEquals(false, b.isGreaterThan(a))
    assertEquals(null, c.isGreaterThan(a))
    assertEquals(null, c.isGreaterThan(c))
}

3.1 创建通用比较函数

如果我们希望这个比较函数适用于所有实现了 Comparable 接口的类型(比如 String),可以将其泛型化:

fun <T : Comparable<T>> T?.isGreaterThan(other: T?): Boolean? {
    return this?.let { it > (other ?: return null) }
}

测试一下整数和字符串的比较:

@Test
fun `compare generic Int comparable types using extension function`() {
    val a: Int? = 5
    val b: Int? = 3
    val c: Int? = null

    assertEquals(true, a.isGreaterThan(b))
    assertEquals(false, b.isGreaterThan(a))
    assertEquals(false, b.isGreaterThan(b))
    assertEquals(null, c.isGreaterThan(b))
}

@Test
fun `compare generic String comparable types using extension function`() {
    val a: String? = "ABC"
    val b: String? = "ZXY"
    val c: String? = null

    assertEquals(true, b.isGreaterThan(a))
    assertEquals(false, a.isGreaterThan(b))
    assertEquals(false, b.isGreaterThan(b))
    assertEquals(null, c.isGreaterThan(b))
}

✅ 这种方式更通用,适用于任何实现了 Comparable 的类型。

4. 使用 Kotlin 的 compareValues() 函数

Kotlin 标准库中提供了一个专门用于比较两个可空值的函数 compareValues()

val result = compareValues(a, b)

⚠️ 与前面方法不同的是:compareValues() 会将 null 视为比任何非 null 值都小。

来看看它的行为:

@Test
fun `compare nullable integers using compareValues function`() {
    val a: Int? = 5
    val b: Int? = 3
    val c: Int? = null

    assertEquals(1, compareValues(a, b))
    assertEquals(-1, compareValues(b, a))
    assertEquals(1, compareValues(a, c))
    assertEquals(-1, compareValues(c, a))
    assertEquals(0, compareValues(c, c))
}

✅ 这个函数的优点是:

  • 不需要手动处理 null
  • 返回值始终是非 null 的整数
  • 认为两个 null 是相等的(返回 0)

这在某些业务场景中非常有用,比如排序时希望 null 排在最前面或最后面。

5. 总结

方法 是否处理 null 返回类型 是否通用 适用场景
compareTo() Int? 简单比较两个可空整数
扩展函数 Boolean? ✅(可泛型) 封装比较逻辑,增强可读性
compareValues() Int 通用比较,适合排序等场景

📌 建议:

  • 如果只是简单的比较,用 compareTo() 即可
  • 如果希望代码更清晰,推荐使用扩展函数
  • 如果要进行排序或统一处理 null 值,优先使用 compareValues()

选择哪种方式,取决于你的具体业务需求和代码风格。合理使用这些方法,可以避免空指针异常,提高代码的健壮性。


原始标题:Using “Greater Than” and “Less Than” Comparison on Nullable Integers in Kotlin