Java の isLeapYear が使えない件

プログラミング中に「そういや今年ってうるう年だっけ」という判定をしなければならないことはままあるかと思います。

Java の場合、java.util.GregorianCalendar クラスに isLeapYear メソッドが用意されているのですが、これがいまいち使いにくい。何故かというと

  • 西暦を整数型で引数として渡す必要がある
  • static メソッドではなくインスタンスメソッドである

という点です。つまり

GregorianCalendar cal = new GregorianCalendar();

で現在の時刻を取得しても結局

cal.isLeapYear(cal.get(Calendar.YEAR));

としてやらないといけません。馬鹿なの ? 死ぬの ?

GregorianCalendar クラスのインスタンスがなければ新たに生成する必要があるので、指定された西暦(例えば 2010 年)がうるう年かどうか調べるにしても

new GregorianCalendar().isLeapYear(2010);

としてやらないと isLeapYear メソッドは使えません。ファック。

で、解決策を考えたわけですが。

解決策 1 : 引数は要求されてもいいから簡潔に書きたい

static boolean isLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}

うるう年の法則さえわかっていれば簡単な条件式で判定できるので、これが一番すっきりします。余計なクラスインポートする必要ないし。あまりにも頻繁に使うようなら適当にクラスを作ってそこにこの static メソッドを入れて随時呼び出せばいいでしょう。

でも中にはいるんでしょうね。「どうせ内部に日付情報保持してるんだから、それをもとに判定させる(引数の要らない)インスタンスメソッドが欲しい !」って人。心配ご無用。我々にはオブジェクト指向があるではないか !

解決策 2 : GregorianCalendar を拡張する

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;

public class MyCalendar extends GregorianCalendar {
    private static final long serialVersionUID = 1L;

    public MyCalendar() {
        super();
    }

    public MyCalendar(int year, int month, int dayOfMonth) {
        super(year, month, dayOfMonth);
    }

    public MyCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute) {
        super(year, month, dayOfMonth, hourOfDay, minute);
    }

    public MyCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second) {
        super(year, month, dayOfMonth, hourOfDay, minute, second);
    }

    public MyCalendar(Locale aLocale) {
        super(aLocale);
    }

    public MyCalendar(TimeZone zone) {
        super(zone);
    }

    public MyCalendar(TimeZone zone, Locale aLocale) {
        super(zone, aLocale);
    }

    public boolean isLeapYear() {
        return this.isLeapYear(this.get(Calendar.YEAR));
    }
}

いっそ既存のクラスを拡張して自分好みにカスタマイズしてしまいましょう。これで上記の MyCalendar を同パッケージ内に置くなり適宜 import するなりして

MyCalendar mycal = new MyCalendar();
boolean isLeap = mycal.isLeapYear();

でよろしい。1 個インスタンス化してしまえば従来の isLeapYear メソッドも(継承しているから)使えるので

mycal.isLeapYear(2010);

のように使いまわすことも可能。

しかしこれだけのために拡張クラス作るのもアホらしい。作ったところで需要がなければ意味がありません。というわけで素直に解決策 1 の方法でメソッド作ってやった方が実務上はよろしいんじゃないかと思われます。

そして Rubyステマをする

さすがは世界の Matz、そんな我々の苦悩を知ってか知らずか、Ruby ではちゃんとメソッドを用意してくれていました。

require 'date'
d = Date.today
puts d.leap?

何この今北産業wwwもうみんな Ruby 使えよwww