[Scala] java.lang.Dateクラスの最も小さなオブジェクトEarlyTimeを定義する

今日は、Javaのオブジェクトを継承して、Scalaのシングルトンオブジェクトを作成してみます。題材は、RakeのEarlyTimeクラスを使ってみます。
EarlyTimeクラスはすべてのDateオブジェクトの中でもっとも小さいオブジェクトで、どんな小さなDateオブジェクトと比較しても必ず小さくなるシングルトンのValueObjectです。

まず、java.lang.Dateを継承して、Scalaのシングルトンを作成します。

import java.util.Date
object EarlyTime extends Date {
  override def compareTo(other:Date) = {-1}
  override def toString = {"<EARLY TIME>"}
}

次に、DateクラスのcompareToを拡張するMixinを定義します。

trait DateMixin extends Date {
  override def compareTo(other:Date) = {
    if (EarlyTime == other) {
      -(other.compareTo(this))
    } else {
      super.compareTo(other)
    }
  }
}

DateMixinでは、compareToメソッドをオーバーライドし、EaryTimeオブジェクトを引数に取るときに、EarlyTimeオブジェクトのcompareTo演算子を呼び出します。また、EarlyTimeオブジェクトと異なる場合には、元のDateオブジェクトのcompareToを呼び出します。

では、実際に評価してみます。

scala> EarlyTime.compareTo(new Date(java.lang.Long.MIN_VALUE))
res1: Int = -1

scala> EarlyTime.compareTo(new Date(java.lang.Long.MAX_VALUE))
res2: Int = -1

scala> 
scala> (new Date with DateMixin).compareTo(EarlyTime)
res3: Int = 1

scala> (new Date(0) with DateMixin).compareTo(EarlyTime)
res4: Int = 1

scala> (new Date(java.lang.Long.MIN_VALUE) with DateMixin).compareTo(EarlyTime)
res5: Int = 1

確かにEarlyTimeは常に小さいオブジェクトになっています。

これだとDateクラスの宣言が使いにくいので、companionオブジェクトを定義します。Scalaではclassと同名のobjectをcompanionオブジェクトと呼び、クラス生成時の便利メソッドを提供する仕組みを用意しています。たとえば、

scala> List(1,2,3)
res1: List[Int] = List(1, 2, 3)

なんかもcompanionオブジェクトだったりします。

では、applyメソッドを持つDate companionオブジェクトを定義してみましょう。

object Date {
  def apply(xs: java.lang.Long) : Date = {
    new Date(xs.longValue()) with DateMixin
  }

  def apply():Date = {
    new Date() with DateMixin
  }
}

java.lang.Longを引数にとるapplyメソッドと、引数なしのapplyメソッドを定義しました。このapplyメソッドは一致する型のメソッドが暗黙的に呼び出されます。

scala> Date(1L)
res18: java.util.Date = Thu Jan 01 09:00:00 JST 1970

scala> Date()
res19: java.util.Date = Sat Apr 11 11:53:04 JST 2009

最後にListでソートしてみましょう。

scala> List(Date(0L), Date(2000L) , EarlyTime, Date() ).sort((e1,e2) => (e1 compareTo e2) > 0)
list: List[java.util.Date] = List(Sat Apr 11 11:53:43 JST 2009, Thu Jan 01 09:00:02 JST 1970, Thu Jan 01 09:00:00 JST 1970, <EARLY TIME>)

正しくソートされましたね。
今日は、java.lang.Dateクラスを拡張し、ScalaのEarlyTimeクラスを定義してみましたがいかがでしょう?なんとなくScalaの拡張性の高さが伺えるサンプルとなったのではないでしょうか?