==と文字列プール
このレッスンでできるようになることは、文字列の == と equals の結果を、文字列プールの仕組みから説明できるようになることです。
まず予測してみます
次の 3 つの出力を決めてください。
String a = "java";
String b = "java";
String c = new String("java");
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a.equals(c));答えは順に true、false、true です。Java Silver で最も出題数が多い論点のひとつです。
文字列プール
ソースに直接書いた "java" のような文字列リテラルは、文字列プールという共有の置き場に 1 つだけ作られます。同じ内容のリテラルが何度出てきても、同じインスタンスが使い回されます。だから a == b は true です。
== は参照型に使うと「同じインスタンスか」を見ます。中身が同じかどうかは見ません。new String("java") は必ず新しいインスタンスを作るので、プールの中の "java" とは別物です。よって a == c は false になります。中身の比較をしたいときは equals を使います。
連結がいつプールに入るか
ここが引っ掛けの本命です。
String x = "ja" + "va"; // true になる
String y = "ja";
String z = y + "va"; // false になる
final String w = "ja";
String v = w + "va"; // true になるリテラルどうしの連結はコンパイル時に 1 つの定数へまとめられるので、プールの "java" と同じインスタンスになります。final を付けた w も定数変数として扱われるので同じです。ところが final の無い変数 y を使った連結は実行時に組み立てられるため、新しいインスタンスになります。final が 1 つ付いているかどうかで答えが反転する、という形で出題されます。
intern
intern() は「この内容でプールに登録されているインスタンス」を返します。実行時に作った文字列でも、intern() を通せばリテラルと == で一致します。
System.out.println(z.intern() == x); // trueただし実務では equals を使えば済むので、intern() を書く場面はほとんどありません。試験用の知識と割り切ってください。
演習
実行時に組み立てた文字列を == で比べているせいで、いつも不一致になるメソッドがあります。直してください。
要件
- same は語の内容が java と等しいかどうかを表すこと
- pooled は語をプールに寄せたうえで、リテラルの java と同一インスタンスかどうかを表すこと
- 戻り値は same と pooled を半角コロンでつないだ文字列にすること
入出力例
match("ja", "va") → "true:true"
match("ja", "x") → "false:false"
match("j", "ava") → "true:true"
match("java", "") → "true:true"