日々常々

ふつうのプログラマがあたりまえにしたいこと。

Calendar.Builderとやらを触ってみた

知らなくても生きていけるけど、知ってるとちょっと便利っぽいもの。 みんな知ってるのかもしれないけど、私は今日まで知らなかったから書いておきます。

もともとは今日 @khasunuma さんがされていた一連のツイートからです。その中で紹介されたスライドに乗っかってたので。

Date and Time APIを使わない場合に間違いなく「使える」と思ったので書いておきます。 というかこんなクラス作ったことあるわ……。

なにものか?

FQCNだとjava.util.Calendar.Builderになります。Calendarクラスにネストされたstaticなクラスです。 追加されたのはJavaSE8になってからなので、それ以前には入ってないです。JavaSE7以前の環境で使いたいと思ったらバージョンあげてください:p

名前の通りCalendarインスタンスを作るものです。 従来の作り方よりも私の趣味には合います。使いやすいかどうかは人それぞれなところもありますので、使ってみて考えてください。とりあえずコードとか置いておくので参考にどうぞ。

Date and Time API使えば要らないんじゃ?

JavaSE8を使うからといって、Date and Time API(JSR310)を使うのが常に正解にはならないと思っています。むしろ現時点のライブラリの対応状況や開発者の理解度、運用実績などを勘案すると使用はチャレンジの領域になってしまう感も否めません。扱える体制が整っているなら使ってった方が良いとは思いますが。

なので、まだしばらくDateCalendarとの付き合いは続くと想像しています。 ということで、Calendarの補助として入ったCalendar.Builderの使い方を覚えておくことは価値があるんじゃないかと思って触ってみました。

コード

Calendar calendar = new Calendar.Builder()
          .setDate(2015, Calendar.MAY, 9)
          .setTimeOfDay(23, 47, 16)
          .build();

これで 2015/5/9 23:47:16 のCalendarインスタンスがつくれます。どうでもいいですけどこの文章を書いている時間です。 第二引数の月は通常のCalendar通り0ベースですので今まで通り基本的に定数を使いましょう。 設定していないところは勝手に今の時間が入るわけではなくカレンダーのデフォルト値(だいたい0)が入るので取り回しは良さそうです。

また、DateインスタンスやEpoc値から設定するsetInstantもあるので、だいたい用は足りそうだなと思いました。

setDatesetTimeOfDayなどのフィールドを設定するメソッドと、setInstantの両方を呼び出すと例外になるところとかも良いですね。インタフェースを工夫すれば片方を呼べばもう一方のメソッドを呼べなくするようなBuilderも作れますが、そこまですると過剰にも思えるのでこれで十分でしょう。

動くコードとどうでもいいコメントは、いつものごとくGitHubの方に置いておきます。

脱線: メソッド名について

GitHubに上げたコードのコメントでも書き殴っていますが、Calendar.Builderメソッド名を単体で見た場合に不満はありません。しかしながらこれがCalendarクラスのbuilderであると考えると、従来のメソッド名との齟齬は気になるところです。

Instantは今までのjava.util.Calendarの世界にはなかった言葉で、使用する際に「Calendar#setTimeをしたいときはsetInstant」という変換が必要になってきます。既存のCalendarに沿うものとして作成されたにしてはこのあたりは少し微妙。

とはいえCalendarクラスのsetTimeメソッドって名前もたいがいアレなんですよね。引数型まで含めて書くとCalendar#setTime(Date)なので、お前は一体何を言っているんだって感じだし。そもそもDate#getTimeが合ってはいるんだろうけどTimeって名前を迂闊に使ったのが云々。長年付き合ってるから慣れてはいるんだけど、見るたびにもんにょりしてます。

個人的趣味ではオーバーロードCalenda.Builder#setInstantのように使うのが好きですが、APIの整合性を崩す方がまずいんじゃ無いかなーと思ったりしつつ、Calendar.Builder#setDateはまさに欲しかった物体なのでsetDate(Date)とする訳にもいか無いからsetInstantでいいのかな、とかもやもやしました。結局は過去とどこで折り合いをつけるかだけの話だと思いますし、現在の選択も悪くは無いと思います。

まとめ

悪くない。少なくともCalendarインスタンスを作る場面では積極的に使いたいと思いました。

JavaSE8でBuilderが入ったり、ファクトリメソッドofができたり、なんかJava標準ライブラリらしからぬものが増えてきているので、長年古き良きJavaに慣れ親しんでいた方々には抵抗があるかもしれないなと思いつつ。

Builderを使ったインスタンス生成が一般的になれば、生成対象インスタンスをイミュータブルにしやすくなるので個人的には嬉しいところです。(そもそもコンストラクタやシンプルなファクトリメソッドインスタンス化できない時点でクラス設計負けてる感はありますが。) イミュータブルが基本になれば、もう少し世界は平和になるはず。と思ってる。