joda timeのDateTimeのコンストラクタがScalaと相性悪い・・・と思ったら単なる勘違いだった件

追記:完全に自分の勘違いでしたすいませんm(_ _)m
結論はこのtogetter見てください(´・ω・`)一応もとのやつは残しておきますが・・・


内容は、表題の通りでなんの面白みもないですが、ハマったというか、未だこれといった直接的な解決策がないのでメモ(´・ω・`)

Joda Timeっていう、日時に関するJavaのライブラリがあるんですが、それのDateTime型のインスタンスを作成しようとしたときの話(ちなみに、Versionはこのblog書いている時点で最新の1.6.2です)

DateTimeのコンストラクタが、丁寧に18個もあるわけですが、その中で以下のようにObject型の引数を1つ取るやつ

public DateTime(Object instant)

がいるわけです。なんでObject型なんて取るのか?というと、どうやら実際はJavaの標準ライブラリのGregorianCalendar型または、Date型のためにあるみたいです。それで、

public DateTime(Date instant)
public DateTime(Calender instant)

というように、2つ以上似たようなコンストラクタをつくるのがめんどくさかったから、引数Object型にしちゃって、中で instanceof で判断すればいいんじゃね?という考えで作成されたのではないかと勝手に予想してますが*1

で、なぜObject型を引数にとるコンストラクタがあると問題になるかという話。そのObject型を引数に取るコンストラクタではなく、年、月、日、時、秒の5つintを引数に取るコンストラクタを使おうとしたとき

public DateTime(int year,
                int monthOfYear,
                int dayOfMonth,
                int hourOfDay,
                int minuteOfHour)

インスタンス作成するには、ScalaでもJavaと全く同じように以下のように書くわけです。

val today = new DateTime(2011,6,22,10,5)

これでなにも問題なさそうですが、REPL*2で試すと、以下のようなエラーがでます

scala> val today = new DateTime(2011,6,22,10,5)
java.lang.IllegalArgumentException: No instant converter found for type: scala.Tuple5
        at org.joda.time.convert.ConverterManager.getInstantConverter(ConverterManager.java:165)
        at org.joda.time.base.BaseDateTime.<init>(BaseDateTime.java:169)
        at org.joda.time.DateTime.<init>(DateTime.java:168)
        at .<init>(<console>:8)
        at .<clinit>(<console>)
        at RequestResult$.<init>(<console>:9)
        at RequestResult$.<clinit>(<console>)
        at RequestResult$scala_repl_result(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at scala.tools.nsc.Interpreter$Reques...

え!?(´・ω・`)

と思ったらどうやら上記の書き方で書いたときに、年、月、日、時、秒の5つ引数をとるコンストラクタが呼ばれるのではなく、Object型の引数を取るコンストラクタにTuple5を渡したと解釈されちゃうようです!

たとえば以下のようなことためしてみましたが、

//一度関数オブジェクトとして保持
val dateConstructor = new Date(_:Int,_:Int,_:Int,_:Int,_:Int)
val today = dateConstructor(2011,6,22,10,5)

結果は同じ・・・

解決策として

  1. 引数を5つ取るコンストラクタのMethodオブジェクトをリフレクションで取得しておく?
  2. Javaで、DateTimeを作成するためのコード書く?
  3. 引数5つのコンストラクタ呼ぶのは諦めて、Scalaから一度引数なしのコンストラクタでDateTime生成して、そのオブジェクトからメソッド呼んで生成?*3
  4. 一度、java.util.Dateか、java.util.Calendarのインスタンスを作成してから、DateTimeのコンストラクタに渡す?などの方法で、他のコンストラクタを使う

などを思いついたのですが、他に何かもっと簡単ないい方法ないかな・・・そもそもREPLでしか試してないので、文脈(?)や呼び出し方によって、Tupleと解釈されるかどうかも不明ですが。

Object型を引数に取るコンストラクタさえなければ・・・(メ゚益゚)ぐぬぬ

ちなみに、英語では似たような質問あるかもしれないですが、まだちゃんと調べてないです(´・ω・`)

  • どちらにしろ日本語の情報が (自分が調べた範囲では) まったくなかった
  • これはScalaずっとやってても結構まれな問題だと思うので、とにかくメモしておいたほうがいいかなぁ

と思って書いておきました

*1:ちゃんと調べてません、誰か知ってたら教えてください

*2:2.8.1でためしたが、2.9.0以降でもたぶん同じっぽい(?)

*3:余計な一時オブジェクトがいっぱい出来るな・・・