KotlinではComparableインタフェースを実装したクラスのオブジェクト同士を比較演算子で直接比較できる

タイトル通りの内容ですが、地味に初めて知ったので備忘録として。


Dateクラスを例として

とあるPull Requestの中に以下のような関数がありました。
引数に渡されたdateを現在時刻と比較し、期限を過ぎているかどうかを判定する関数です。

fun isExpired(date: Date): Boolean {
  return date > Date()
}


結果として、この関数の比較結果が正しいことは分かったのですが、Dateオブジェクト同士を比較演算子で直接比較できていることに個人的には違和感がありました。
以下では、この件について調べた内容を記載しています。


比較演算子compareTo()メソッドに変換される?

IDE>の宣言を参照すると、Dateクラス内のcompareTo()メソッドが表示されました。
このcompareTo()メソッドはComparableインタフェースの実装です。
(できれば@overrideアノテーションを付けておいてほしい…)

public class Date implements Serializable, Cloneable, Comparable<Date> {

  ...

  public int compareTo(Date anotherDate) {
    long thisTime = getMillisOf(this);
    long anotherTime = getMillisOf(anotherDate);
    return (thisTime < anotherTime ? -1 : (thisTime == anotherTime ? 0 : 1));
  }

  ...

}


このことから、Kotlinの比較演算子は、比較対象のクラスのcompareTo()メソッドに変換されるであろうことが分かりました。
ただ、compareTo()メソッドの戻り値はInt型で、単純な変換ではBoolean型にならずコンパイルエラーになるはずなので、Kotlinの比較演算子の変換についてさらに調べてみました。
(この時点では「何故after()メソッドではなくcompareTo()メソッドなんだろう?」と思っていました笑)


比較演算子は「compareTo()メソッド + 期待される値の範囲」の組み合わせに変換される

公式ドキュメント演算子オーバーロードの項目を確認すると、Kotlinの比較演算子は、比較対象のクラスのcompareTo()メソッド + 期待される値の範囲」の組み合わせに変換されるようでした。

f:id:tkhs0604:20210109010748p:plain:w300
公式ドキュメントより引用


試しに冒頭の関数のバイトコードデコンパイルしてみたところ、確かに上記の表の式の形に変換されていました。

public boolean isExpired(@NotNull Date date) {
  Intrinsics.checkNotNullParameter(date, "date");
  return date.compareTo(new Date()) > 0;
}


結果として、KotlinではComparableインタフェースを実装したクラスのオブジェクト同士を比較演算子で直接比較できることが分かりました。