Java好き

カテゴリ:Apache > Commons Lang

「DateUtils」は、Commons Langのクラス。
DateやCalendar周りの日付操作ユーティリティ。

DateやCalendarは基礎的な操作は提供するが、
年月日のみの比較というような 実際によく行われる処理についてはない。
このDateUtilsはそこを補う。

使い方

年月日のイコールはisSameDay()

Date型のequals()は、
ミリ秒まで一致しなければtrueにならない。

よって年月日のみ同じかどうか調べる場合は、
年と月と日をそれぞれ取り出して比較しなくてはいけない。

そんなときは、このDateUtils.isSameDay()で一発。

long currentTimeMillis = System.currentTimeMillis();
Date firstDate = new Date(currentTimeMillis);
Date secondDate = new Date(currentTimeMillis + 1);//比較のためミリ秒をずらしておく
 
//ミリ秒が一致しないのでfalseになる
boolean isEqual = firstDate.equals(secondDate);
 
//年月日が同じなのでtrueになる
boolean isSameDay = DateUtils.isSameDay(firstDate, secondDate);

ばっさりを切り捨てるtruncate()

Date型はミリ秒まで扱うが、
年月日のみ扱いたい場合も多い。

こういった場合、時分秒などは0に設定しておきたいが
面倒な作業である。

そんなときは1つ1つ設定するのではなく、
truncateでばっさり切る(0にする)。

Calendar cal = new GregorianCalendar(2002, 2, 28, 13, 45);
  
//日より下が切り捨てられるので「2002年3月28日0時0分0秒」となる
Date truncated = DateUtils.truncate(cal.getTime(), Calendar.DATE);

フィールド定数とさよならしたい

add()やset()はCalendarで提供されているが、
フィールドに関する定数値を指定しなければならない。

これをCalendarクラスの膨大な定数の中から選ぶのがたいへんで、
違うフィールドを選んでしまっても同じint型なので コンパイルエラーにはならない。

DateUtilsではこれらのフィールドに関する操作をメソッドで提供してる。
また、Date型で受け取ってくれるので いちいちCalendar型に変換する必要もない。

//通常のやり方
Date date = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.YEAR,1);
 
//DateUtilsで簡単に
Date date = new Date();
DateUtils.addYears(date, 1);

「Validate」は、Commons Langのクラス。
引数チェックを一行で書くことができる。

メソッドにおいて引数チェックは欠かせないが、
if文が含まれるためどうしても複数行になる。

通常の引数チェック
public void someMethod(String arg){
    if(arg == null | | !arg.equals("")){
        throw new IllegalArgumentException("arg is null");
    }
}

そんな引数チェックを一行に収めるためのメソッドが
「Validate」クラスに用意されている。

使い方

引数チェックを一行で書く

Validateクラスのメソッドを使うだけでよい。
デフォルトでメッセージも設定してくれる。(自分で指定もできる)

これで膨れあがっていた引数チェックのコードを
削減できる。

Validateを利用した引数チェック
public void someMethod(String arg){
    Validate.notEmpty(arg);
}

Vadliteでは、さまざまな種類のバリデーションメソッドを提供する。(表は主なもののみ)

メソッド チェックの内容
notNull(T object) nullの場合、例外
notEmpty(T chars) 文字列がnullか""の場合、例外
notEmpty(T collection) コレクションがnullか空の場合、例外
matchesPattern(CharSequence input,String pattern) 正規表現にマッチしない場合、例外
isInstanceOf(Class type,Object obj) typeのクラスのインスタンスではない場合、例外

「ReflectionToStringBuilder」は、Commons Langのクラス。
リフレクションを用いてフィールドの内容を取得するので
toString()メソッドを1行で書くことができる。

クラスの状態をログに書き出す場合など、
toString()を定義しておくと便利。

ただ、新しいクラスやフィールドの変更で
毎回toString()を書き直すのは心が折れる。

そこで、リフレクションを使ってフィールドの内容を出力する
ReflectionToStringBuilderが便利。

使い方

1行加えるだけ

次のようにtoString()をオーバーライドするだけで簡単に使える。

@Override public String toString() {
    return ReflectionToStringBuilder.toString(this);
}
出力
Person@182f0db[name=John Doe,age=33,smoker=false]

出力形式をカスタマイズする

デフォルトの出力内容が気にいらないときは、
ToStringStyleを指定することで出力形式を変更することができる。

ToStringStyleにはいくつか定義済みのものがあるので
それを使う。

例.ToStringStyleの設定
@Override public String toString() {
   final ToStringStyle style = ToStringStyle.SIMPLE_STYLE;
   return ReflectionToStringBuilder.toString(this,style);
}
主なスタイル
スタイル 出力形式
ToStringStyle.DEFAULT_STYLE Person@182f0db[name=John Doe,age=33,smoker=false]
ToStringStyle.NO_FIELD_NAMES_STYLE Person@182f0db[John Doe,33,false]
ToStringStyle.SHORT_PREFIX_STYLE Person[name=John Doe,age=33,smoker=false]
ToStringStyle.SIMPLE_STYLE John Doe,33,false

スタイルの他にも、出力する項目などある程度自由に設定することが可能。

注意

循環参照に注意する

リフレクションを使っている性質上、
循環参照に注意しなくてはいけない。

例えばA→B・B→Aのような参照があるクラス同士の場合は、
循環参照になり無限ループになってしまう。

このような場合は、setExcludeFieldNames()メソッドをもちいて
toString()の対象から除外することで回避できる。

またsetExcludeFieldNames()は、
パスワードなど意図的に伏せたいフィールドを除外するときなども利用できる。

@Override public String toString() {
    ReflectionToStringBuilder builder = new ReflectionToStringBuilder(this);
    builder.setExcludeFieldNames("password");
    return builder.toString();
}
 

「FastDateFormat」は、Commons Langのクラス。
SimpleDateFormatのスレッドセーフ対応版。

「fast」とあるが、内部キャッシュなどの工夫もあって
SimpleDateFormatより若干fast。

フィールドにはSimpleDateFormatを使っていけない

SimpleDateFormatは非スレッドセーフ。

にわかには信じがたいが、
高負荷をかけると例外を発生させることができる。
(JavaDocにも書いてある)

よって同じ変換を毎回する場合でも、
フィールドで保持するわけにはいけない。

これに対処するために、SimpleDateFormatのスレッドセーフ対応版として
FastDateFormatが存在する。

注意しておくこと

SimpleDateFormatより優れているFastDateFormatを無条件に使いたいが、
欠点もある。

  • 和暦に対応していない

残念だけど和暦としてフォーマットできない。

また、以前はparseに対応していなかったが
バージョン3.2以降はparseに対応している。

使い方

コンストラクタはprotectedになっているので、ファクトリメソッドを使って生成する。

private static final FastDateFormat format = FastDateFormat.getInstance("yyyy/M/d");

「Commons Lang」とは

「Commons Lang」は、Apache commonsのライブラリ。

JavaSE標準ライブラリにおいて
不便であったりで足りない機能を補うクラスが
数多く提供されている。

例えば、Dateクラスは標準ライブラリに存在するが使いにくい。
というように標準ライブラリのjava.lang周りを補完するものを提供する。

このようなライブラリでほかの有名どころにGuavaがある。
どちらを使うか考えるかも知れないが両方使える。

Guavaとも同じようなクラスも存在はするが被っていない機能も多い。
共に副作用の強いクラスはほとんど無いので、両方ライブラリとして追加してもよい。

また、「Commons Lnag」は便利なライブラリといった側面のほかにも
ユーティリティ(ライブラリ)の設計として見てみると
おもしろく、参考になる。

こういったユーティリティはついなんとなく作ってしまいがちだが、
「Commons Lnag」には真似したい(取り入れたい)考えが多い。

「nullセーフ」であったり、
「JavaDocの書き方」であったり、
「内部キャッシュ」であったり
勉強になる。

扱っている機能自体がそこまで難関なものではないので
解析がしやすい。(もちろんオープンソース)

便利として使う以外にも、こういったところも参考にしたい。

このクラスだけでも使いたいクラス

Commons Langの中でも特徴的で、このクラスを使いたいがために
Commons Langをライブラリに組み込みたいと思えるクラスをいくつか紹介する。

StringUtils

StringUtilsはStringに関するユーティリティメソッドを提供する。
それは全てnullセーフになっている。

文字列操作に関するメソッドを使うとき
Stringがnullの場合、即例外になってしまう。

すぐに例外になっても困ることが多いし、
nullチェックを入れるとコードが見にくくなるので
StringUtilsは重宝する。

nullセーフであることはStringUtilsだけでなく
Commons Lang内の他ユーティリティでも
採用されている思想である。

Stringクラスを用いたトリム
String trimedStr;
if (str != null) {
  trimedStr = str.trim();
} else {
  trimedStr = null;
}
StringUtilsを用いたトリム
String trimedStr = StringUtils.trim(str);

またStringUtilsは提供するメソッドが
とても豊富でざっと25種類180個以上もある。
(バージョン3.3.2現在)

例えば、subStringだけでもこれだけある。

substring関係のメソッド
substring(String str, int start)
substring(String str, int start, int end)
substringAfter(String str, String separator)
substringAfterLast(String str, String separator)
substringBefore(String str, String separator)
substringBeforeLast(String str, String separator)
substringBetween(String str, String tag)
substringBetween(String str, String open, String close)
substringsBetween(String str, String open, String close)

とにかく数も種類も多いので、
文字列関連のユーティリティを探すとたいてい見つけることができる。

FastDateFormat(Commons Lang API)

ContextedRuntimeException

ContextedRuntimeExceptionは
例外オブジェクトに情報を追加できる機能をもったクラス。

例外を発生させる場合に、
1つの文字列の中にすべての情報を入れるのは難しい。

なので簡単なメッセージを付け加えるだけしかできず
大切な例外時の状況を伝えることができない。

そこでContextedRuntimeExceptionは
ラベルと値というかたちで、
いくつでもそのときの状況を付加することができるのでわかりやすい。

int employId = 1;
String name = "Taro";
int baseSalary = 300000;
 
try {
    // 何らかの処理
} catch (Exception e) {
    throw new ContextedRuntimeException("給与計算中に例外が発生。", e)
        .addContextValue("従業員ID", employId)
        .addContextValue("名前", name)
        .addContextValue("基本給", baseSalary);
}

独自例外を作成する場合も
ただRuntimeExceptionを継承するのではなく
ContextedRuntimeExceptionを継承しておけば
こんな機能が簡単に利用できる。

ContextedRuntimeException(Commons Lang API)

DateUtils

Date型の日付計算やCalendar型との変換など面倒なことが多い。
DateUtilsはそのいくつかを補うことができる。

通常のDate型の日付計算
Date date = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.YEAR,1);
DateUtilsを用いた日付計算
Date date = new Date();
DateUtils.addYears(date, 1);

JavaSE8からDateTime APIが登場しているが
Date型はおそらくまだまだ現役だろう。

そういったときに、DateUtilsのメソッドはありがたい。

DateUtils(Commons Lang API)