如何创建一个 Kotlin 可比较类型?

22

刚学习如何定义日期范围类型

val wholeYear2017 = Date(2017,1,1)..Date(2017,12,31)

所以我创建了以下类型

class DateRange<Date: Comparable<Date>>(override val start: Date, override val endInclusive: Date)
    : ClosedRange<Date>

class Date (val year: Int, val month: Int, val day: Int) {

    operator fun compareTo(other: Date): Int {
        if (this.year > other.year) return 1
        if (this.year < other.year) return -1
        if (this.month > other.month) return 1
        if (this.month < other.month) return -1
        if (this.day > other.day) return 1
        if (this.day < other.day) return -1
        return 0
    }

    operator fun rangeTo(that: Date): DateRange = DateRange(this, that)
}

但是我遇到了编译错误

One type of argument expected for class DateRange<Date: Comparable<Date>> : ClosedRange<Date>

我错过了什么?我做得对吗?

5个回答

43

您需要实现Comparable接口。您可以使用compareValuesBy辅助函数:

data class Data(
        val a: Int,
        val b: Int
) : Comparable<Data> {
    override fun compareTo(other: Data) = compareValuesBy(this, other,
            { it.a },
            { it.b }
    )
}

7
那应该是被接受的答案。迄今为止最优雅的解决方案! - helpermethod

33

你的问题是否真的与如何创建可比较类型有关?那么只需让你的类型实现Comparable接口(覆盖compareTo)即可。

class Date(val year: Int, val month: Int, val day: Int) : Comparable<Date> {
    override operator fun compareTo(other: Date): Int {
        if (this.year > other.year) return 1
        if (this.year < other.year) return -1
        if (this.month > other.month) return 1
        if (this.month < other.month) return -1
        if (this.day > other.day) return 1
        if (this.day < other.day) return -1
        return 0
    }
 }

你不需要一个rangeTo方法,因为所有的Comparable<T>类型已经定义了rangeTo扩展。请参见RangesrangeTo。但是,如果你仍然希望拥有自己的DateRange类型(用于其他目的),则DateRange类的简化形式为...

class DateRange(override val start: Date, override val endInclusive: Date)
    : ClosedRange<Date>

换句话说,DateRange 上不需要泛型参数。
接下来您需要编写自己的 rangeTo 运算符。要么将 operator fun rangeTo 添加到您的 Date 类中,要么提供一个根级别的扩展函数(这是我更喜欢的方式,与 Kotlin 库的方法一致)。两者都会为您的 Date 类型隐藏 Comparable<T>.rangeTo 扩展函数。
// class level rangeTo operator
operator fun rangeTo(that: Date) = DateRange(this,that)

// root level extension
operator fun Date.rangeTo(that: Date) = DateRange(this,that)

我无法执行 class DateRange(override val start: Date, override val endInclusive: Date): ClosedRange<Date> . 它会报错 Type argument is not within its bounds: should be subtype of 'Comparable<Date>' - Elye
所以,这就是我使用 class DateRange<Date: Comparable<Date>>(override val start: Date, override val endInclusive: Date) : ClosedRange<Date> 来解决编译错误的原因。 - Elye
1
如果您在Date类中添加: Comparable<Date>,那么您的DateRange类就不需要泛型参数了。 - Les
确实。谢谢! - Elye

4

您已经接近成功:

首先,您必须使您的类实现 Comparable 接口:

class Date (val year: Int, val month: Int, val day: Int): Comparable<Date> {

其次,您必须指定返回类型的泛型,或者省略它(让编译器推断)。
operator fun rangeTo(that: Date): DateRange<Date> = DateRange(this, that)
operator fun rangeTo(that: Date) = DateRange(this, that)

1
谢谢!我还需要在“fun operator compareTo”中添加“override”关键字。 - Elye

1

这段内容来自Koans,我非常鼓励你先了解文档。


注:该段内容为IT技术相关内容。
override fun compareTo(other: Date) = when {
        year != other.year -> year - other.year
        month != other.month -> month - other.month
        else -> day - other.day
    }

2
通过减法比较值容易受到整数溢出的影响:避免使用减法技巧 (Baeldung) - Gabriel Pizarro

0
您可以使用以下公式将日期转换为整数表示形式,例如:年份*10000 + 月份*100 + 日。
class Date (val year: Int, val month: Int, val day: Int) {

    operator fun compareTo(other: Date): Int {
        return (this.year * 10000 + this.month * 100 + this.day) - (other.year * 10000 + other.month * 100 + other.day)
    }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接