How to make a Kotlin Comparable Type?

Viewed 16963

Just learning to define a DateRange type

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

So I created the type as below

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)
}

But I got a compile error

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

What did I missed? Did I do it correctly?

5 Answers

Is your question really about how to create a Comparable type? Then just have your type implement the Comparable interface (override 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
    }
 }

You don't need a rangeTo method because all Comparable<T> types have a rangeTo extension already defined. See Ranges and rangeTo. But, if you still want your own DateRange type (for other purposes), the simpler form of the DateRange class would be...

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

In other words, there is no need for the generic parameter on DateRange.

Then you would write your own rangeTo operator. Either, add operator fun rangeTo to your Date class, or provide a root level extension function (my preference which is consistent with the Kotlin library approach). Both will shadow/hide the Comparable<T>.rangeTo extension function for your Date type.

// class level rangeTo operator
operator fun rangeTo(that: Date) = DateRange(this,that)

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

You are almost there:

Firstly, you must make your class Comparable:

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

Secondly, you must specify the generic of the return type or just omit it (let the compiler infer it)

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

Straight from Koans. I really encourage you to get to know the documentation first.

override fun compareTo(other: Date) = when {
        year != other.year -> year - other.year
        month != other.month -> month - other.month
        else -> day - other.day
    }

You can translate date to the Integer representation using such formula, for example: year * 10000 + month * 100 + day.

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)
    }
}

Related