java.lang.Stringクラスのequalsメソッドのソースコードを読んでみた
ソースコード
バージョンは1.6。ソースコードを読むのは初めてなので、間違いは多分いっぱいある。
public boolean equals(Object anObject) {
// (1)
if (this == anObject) {
return true;
}
// (2)
if (anObject instanceof String) {
String anotherString = (String)anObject;
// (3)
int n = count;
// (4)
if (n == anotherString.count) {
// (5)
char v1[] = value;
char v2[] = anotherString.value;
// (6)
int i = offset;
int j = anotherString.offset;
// (7)
while (n-- != 0) {
if (v1[i++] != v2[j++]) {
return false;
}
}
return true;
}
}
return false;
}
解説メモ
(1) this == anObject
this は該当クラスのオブジェクトを参照する参照型変数である。 コンストラクタ内、初期化ブロック内、フィールド宣言の初期化子内の this 変数は生成中のオブジェクトを参照する。 メソッド内の this 変数は、メソッド呼び出し対象のオブジェクト(レシーバ)を参照する。
つまり、equals()メソッドのレシーバと引数の anObject が等しい場合に true を返すことになる
String string = new String("string");
if (string.equals(string)) {
System.out.println("レシーバと引数は同一である。よって必ず同値になる");
} else {
System.out.println("not equals");
}
(2) instanceof (パーフェクト Java P240参照)
instanceof 演算子は、オブジェクトの型を判定する2項演算子。左オペランドがオブジェクト参照で、右オペランドが参照型の型名になる。 評価値の型は boolean。左オペランド式が、右オペランドの型に代入可能な参照型の時、式の評価値が真になる。代入可能でない場合は偽になる。 別名「型比較演算子」。
・instanceof 演算子の目的 instanceof 演算子はダウンキャストと密接に関連する。instanceof 演算子を使う目的はダウンキャストを安全に行えるかを事前にチェックするためである。
void doit(Object obj) {
// 変数objがString型に代入可能であればobjのダウンキャスト(Object型→String型)をする
if (obj instanceof String) {
String s = (String) obj; // ダウンキャスト
System.out.println(s);
}
}
※ 型変換・キャスト
- キャストとは、明示的に型を変換すること。javaの場合は、カッコの中に型名を書き、つづけて変数を書くことで変数の型を変換することができる
- 拡大変換:型をより大きなサイズの型に変換すること(例:intをlongにする)。拡大変換はキャスト無しで変換することができる。
- 縮小変換:型をより小さなサイズの型に変換すること(例:longをshortにする)。縮小変換はキャストが必要になる。
- アップキャスト:あるクラスのスーパークラスへと型を変換すること
- ダウンキャスト:あるクラスのサブクラスへと型を変換すること
- アップキャストは安全だが、ダウンキャストは必ずしも安全とは言えない。むしろ避けるべき動作である。
- 互換性が無い型同士でキャストを行うと、「java.lang.ClassCastException」例外が発生する
(3) int n = count
count は、String クラスのインスタンスフィールド String.java 102行目:“private final int count;” コメントには「The count is the number of characters in the String.」と書かれているので、「文字列内の文字数」という解釈で合っているだろう。
またここで気がついたことだが、String.java はコンストラクタが多数オーバーロードされている。標準クラスは大抵こんなものなのかな? これに関しては リファレンス を参照するとよくわかる
(4) n == anotherString.count
ここではレシーバと引数、それぞれのオブジェクトのフィールド「count」を用いて、文字数の比較をしている。 ここで一致しなければ false を返して終了
(5) char v1[] = value
レシーバの String オブジェクトの文字列値を char 型の v1配列に格納 パラメータの String オブジェクトの文字列値を char 型の v2配列に格納 例えば、
String s = new String("abc");
のように宣言された変数 s であれば、 {"a","b","c"} のようになっているのだと思われる
(6) int i = offset
整数型変数 i にレシーバの String オブジェクトの offset 値を、同じく整数型変数 j に equals()メソッドの引数に指定された String オブジェクトの offset 値を代入する offset は、いまいちよくわからなかったが、調べた結果、v1[]と v2[]に格納した文字列の開始位置のことらしい。
(7) while (n— != 0) ..
配列に格納した文字のセットを、1つずつ比較する。 1つでも異なった場合は false を返す。全て合致した場合は true を返す。 while の条件文にデクリメントを使っているのは、新鮮だった。
その他
オーバーロード
- メソッドのオーバーロード : 同一クラス内でメソッド名が等しく、かつ引数の型・数・並び順が異なるメソッドを複数定義すること
- コンストラクタのオーバーロード : 引数の並びが異なるコンストラクタを複数定義すること
コンストラクタのオーバーロードにおける this 呼び出しと super 呼び出し
- this()メソッドを用いることで、同じクラス内のコンストラクタを呼び出すことができる
- これにより、それぞれのコンストラクタの共通部分をくくり出すことができる
class Book {
String title;
int price;
String author;
Date published;
Book(String title, int price, String author, Date published) {
this.title = title;
this.price = price;
this.author = author;
this.published = published;
}
Book(String title, int price, String author) {
// this()メソッドで上述のコンストラクタを呼び出す
this(title, price, author, new Date());
}
}
同様に、super()メソッドを用いることで、クラスを拡張継承したときに、継承したクラスから継承元のコンストラクタを呼び出すことができる。
class ProgrammingBook extends Book {
String language;
// 継承したProgrammingBookと継承元のBookではlanguageが増えただけなので、
// 下記のように書くことでコードの重複をなくすことができる
ProgrammingBook(String title, int price, String author, Date published, String language) {
super(title, price, author, published);
this.language = language;
}
equals メソッドの基本的な情報(API リファレンスから)
- オーバーライド : クラス Object 内の equals メソッド
- パラメータ : anObject - この String と比較されるオブジェクト
- 戻り値 : boolean。指定されたオブジェクトがこの文字列に等しい String を表す場合は true、そうでない場合は false
equals メソッドと「==」演算子について
equals メソッドと==演算子は明確な違いがあり、これは同一性と同値性という概念として区別しておくと良い。
- 同一性 : 2つのオブジェクト参照が同一のオブジェクトを参照している時に真になる比較
- 同値性 : 同値性の比較基準はオブジェクト毎に異なるが、例えば文字列であれば2つの文字列の内容が一致していれば真になる。数値であれば2つの数値が一致していれば真になる。
同一性が真であれば、かならず同値性も真になる。しかし逆は必ずしも成り立たない。 同一性の比較には、等値演算子である「==」または「!=」を用いる。 同値性の比較には、文字列の場合は、equals メソッドを用いる。