概要
皆さんの Repository にこんな定数はありませんか?
/** 1日を表すミリ秒数. */
public static final int ONE_DAY = 86400000;
あるようでしたら以下をご覧ください。
指摘
/** 1日を表すミリ秒数. */
public static final int ONE_DAY = 86400000;
見れば見るほど突っ込みどころのある素敵な定数ですね。
- 型が long でなく int
- 一目で見て何ミリ秒なのかわかりにくい
- 定数名から単位がわかりにくい
修正案1
では、こうするのはどうでしょうか?
/** 1日を表すミリ秒数. */
public static final long ONE_DAY_MS = 86400 * 1000L;
- 型を long に変更した
- 1000 をかけてミリ秒であることをわかりやすくした
- 定数名に単位(ms)を追加した
多少わかりやすくはなりました。が、最初の1回だけとはいえあまり必要性を感じない乗算が発生するのは微妙な感じですね。 間違った認識でした。後述します。
修正案2
Java SE7 からは整数リテラルの桁区切りに _ を含めることができるようになりましたので、それを使ってみましょう。
/** 1日を表すミリ秒数. */
public static final long ONE_DAY_MS = 86_400_000L;
指定したミリ秒数が 8,640万であることはわかりやすくなりました。ですが、そもそも 86,400 が1日の秒数だとすぐわかる方はそんなに多くないかもしれないですね。24 * 60 * 60 * 1000L と指定すれば多少わかりやすくなりまするのかもしれませんが、乗算が復活します。
(追記) @shiracamus さんがコメントで指摘してくださったように、乗算でリテラルを定義した場合はコンパイル時に最適化が実施されます。定数(private static final)で定義すればインライン展開されるので、パフォーマンス上の問題はありません。
TimeUnit
今回の場合、Java SE 1.5 で追加された java.util.concurrent.TimeUnit を使うといい具合に指定できます。
/** 1日を表すミリ秒数. */
public static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1L);
enumの時間単位を選択し、それを基準として各単位の duration に変換します。上記の例ですと 「1」「DAYS」を単位「Milli(Second)s」で変換しています。単に数値を記述してコメントで補うよりも、どれくらいの期間を指定しているのかコードからわかりやすくなっています。自己文書的なコードを目指す上ではよい修正です。
仮に1日が半日になった場合でも下記のようにすればOKです。
/** 半日を表すミリ秒数. */
public static final long ONE_DAY_MS = TimeUnit.HOURS.toMillis(12L);
使用できる時間単位
1日から1ナノ秒までサポートしています。
時間単位 | 説明 | 変換メソッド |
---|---|---|
DAYS | 1日(24時間) | toDays(duration) |
HOURS | 1時間(60分) | toHours(duration) |
MINUTES | 1分(60秒) | toMinutes(duration) |
SECONDS | 1秒(1,000ミリ秒) | toSeconds(duration) |
MILLISECONDS | 1ミリ秒(1,000マイクロ秒) | toMillis(duration) |
MICROSECONDS | 1マイクロ秒(1,000ナノ秒) | toMicros(duration) |
NANOSECONDS | 1ナノ秒 | toNanos(duration) |
まとめ
ある単位での時間を整数値で保持、または指定したい場合は TimeUnit の使用を検討しましょう。コードの可読性が向上します。
(追記) ただ、パフォーマンス上では private static final の定数を乗算で定義した方が有利のようです。
参考
OpenJDK 8u40-b25 での java.util.concurrent.TimeUnit の実装
『Effective Java』第2版で紹介されている enum の 定数固有メソッド実装 (constant-specific method implementation) がされているようです。コンパイラのアシストにより必要なメソッドの実装漏れを防ぐことができるパターンですので、覚えておくと今後の開発に役立ちそうな感じがします。
閑話
私は下記のクラスの実装を見て今回の記事を書くに至りました。
https://github.com/playframework/play1/blob/7dcda9599d532542b17ae10d591d8a76f5e464fd/framework/src/play/libs/Time.java#L19