プログラミング第2補足

シーザー暗号のバグでクソ萎えしたので補足を書きます。授業はJava1.4で進めていることを断っておきます。

public class Point2 {
  private int x, y;
  public boolean equals(Point2 o) {
    // 埋めろ
  }
}

toStringとequalsをオーバーライドしろとありますが、Object#equalsのシグネチャはpublic boolean equals(Object o)です。引数の型が違うのでオーバーライドできていません。ArrayListだのHashMapだのに放り込むと一部の動作に不具合が出ます(Set/Map系だとゴミクズ化する)。

@Override
public boolean equals(Object o) {
	if (o instanceof Point2) {
		Point2 p = (Point2) o;
		return x == p.x && y == p.y;
	} else {
		return false;
	}
}

このようにしてください。
@OverrideはJava5で追加された機能であるアノテーションの1つで、メソッド名や引数が違っていてオーバーライドに失敗し、単に新しいメソッドを追加しただけになる非常に面倒なバグを抑制します(ちょうど今回のように…)。Point2 oのまま@Overrideをつけるとエラーになるはずです(それが仕事です)。クラスの中(メソッドの中ではなく)でCtrl+SpaceでEclipseの入力支援を呼び出し、適当なメソッドのオーバーライドを選んでEnterを押すと@Overrideも勝手につけてくれるはずです。
equalsの中ではまずinstanceofでそのクラスのオブジェクトかどうかを判定します。oがnullのときはinstanceofがfalseを返し、equalsがfalseを返すことに注意してください。その後、安全にキャストし、equal判定を行います。これでNullPointerExceptionやClassCastExceptionなどのRuntimeExceptionを発生させることなく判定ができます。


もう1つの課題、Dateクラスの実装でのequalsも同様です。が、こちらはさらにComparableインタフェースの実装というタスクもあります。

class Student implements Comparable {
  int compareTo(Object o) {
    if (this.height > o.height) return 1;
    else if (this.height == o.height) return 0;
    else return -1;
  }
}

授業のスライドですが、これ、コンパイルエラーです。Object oなのにo.heightなんてありません(授業ぶっぱしておいて何言ってんだか…)。比較の前にoをStudentにキャストしてください。あと、APIドキュメントに書いてありますが、大小関係を-1, 0, 1で返す必要はありません。負の整数・ゼロ・正の整数を返します。スライドのcompareToの説明は半分嘘です。

public class Date implements Comparable<Date> {

  private int day, month, year;

  @Override
  public int compareTo(Date date) {
    if (year != date.year) {
      return year - date.year;
    } else if (month != date.month) {
      return month - date.month;
    } else {
      return day - date.day;
    }
  }

}

小さい順に並べたいときは(自分)-(相手)なのはプログラミング言語を問わず共通なので丸覚えしておくといいかも。

// 1.4
public interface Comparable {
  int compareTo(Object o);
}
// 5
public interface Comparable<T> {
  int compareTo(T o);
}

5で追加されたGenericsという機能により、Comparableは互換性を保ちつつ上のように修正されました。意味はソースから推測してください。キャストが全く必要なくなっているのが利点です。C++のテンプレートを見た目だけパクったもので、外部では論理的な正確さを補強し、内部では勝手にキャストが追加されているだけというまったくクソくない機能です。


今回はAPIについてということだったようですが、スライドに書かれているもの関連で(オーバーライド・コレクションフレームワーク・ラッパークラス)、アノテーションGenerics・オートボクシングなどのJava5で画期的な機能が追加されているのでちょっと調べてみると吉。


ついでにStringBufferは文字列と他の何かの足し算を書いたときに自動的にそれを使うコードが生成されるというものでしたが、Java5でStringBuilderに取って代わられました。Bufferはスレッドセーフ(ゆえのオーバーヘッドあり)でしたが、文字列の連結にスレッドセーフティは必要ないことも多かったことからです。特に+による連結コードの変換結果としてはスレッド安全性は不要なのでコンパイラはStringBuilderを使ったコードを吐くようになりました。これからはスレッド安全性が必要でなければStringBuilderを使ってください。


以上、種籾Javaプログラマ向け補足でした。Javaはしっかりやればすぐにモヒカンになれるよ!


追記

Date[] array = new Date[5];
array[0] = new Date(31, 8, 2000);
array[1] = new Date(3, 3, 2010);
array[2] = new Date(2, 3, 2010);
array[3] = new Date(3, 11, 2010);
array[4] = new Date(3, 9, 1999);
Arrays.sort(array);
System.out.println(Arrays.toString(array));

配列の出力にはArrays.toString(Object[] a)がおすすめです(他にもオーバーロードがあります)。

for (Date d : array) {
  System.out.println(d);
}

拡張for文(Java5)が便利な場合もあり(今回はArrays.toString()でしょうが)。