なぜJavaで「==」で文字列比較できないのか
プリミティブ型とクラス型について
はじめに
今回は「プリミティブ型とクラス型」についてまとめたいと思います。
なぜこの内容を選んだのかというと、昔Javaを書いている時に
なぜJava文字列比較で「==」を使えないのか?
という疑問が生まれたのです。これが最近、研修を受けることによって復活したためです。
当時はまだプログラミングを始めたばかりということで「できないんだー」くらいにしか思っていませんでした。そして年数を重ね、だいぶ分かるようになってきたところでJavaの研修で再び「文字列比較に「==」は使えない」と学んだ。
説明はされたが、気になったので調べようと思います。
「==」が使えないのか確認
では、なぜ「==」での比較ができないのでしょうか。変数として使っているintやdoubleでは当たり前のように「==」を使うことができます。同じ変数のなら使えるはず、、、
実際、「==」を使ってコードを書いてもコンパイルエラーにはなりません。そう、「==」が使えないわけではないのです。ためしに以下のコードを書いて実験してみましょうか。
String a = "abc"; String b = "abc"; if( a == b ) { System.out.println( "同じ" ); } else { System.oiu.println( "違う" ); }これを実行すると、「同じ」と出力されたはずです。ちゃんと比較できていますね!!
では次にコードを書き換えてみましょう。
String a = "abc"; String b = "ab"; b += "c"; if( a == b ) { System.out.println( "同じ" ); } else { System.out.println( "違う" ); }これを実行すると、「違う」と出力されます。
あれ?
できあがる文字列bは「abc」となって、aと同じ文字列のはずです。しかし、上記のコードでは異なると判定されています。
なぜでしょうか。この現象が起こるのでしょうか。
原因としては、Stringがクラスであるからです!!
プリミティブ型とクラス型
Stringって実はクラスなんですよね。だからStringって大文字はじまりなんですね!これについては先日まとめましたね。詳しくはこちら
クラスならインスタンス化しないの?
そうですよね、Stringがクラスなら宣言時にインスタンス化を行わないと使えないはずです。しかし、普段Stringをインスタンス化して使うことはないですよね。
どうやらStringはよく使うもののため、インスタンス化しないで使えるようになっているそうです。
ではプリミティブ型とクラス型についてまとめます。
プリミティブ型の変数は、よく言われている箱のイメージで問題ありません。
変数という箱を作り、値を入れておきます。変数の比較をするときは箱の中にある値を比較します。
そのため、計算を行ったあとの変数を比較しても正確に結果が返ってきます。
では、以下のコードを実行してみましょう。
class Test {
public static void main( String[] args ) {
int a = 1;
int b = 1;
System.out.println( "a=" + a + " b=" + b );
if( a == b ) {
System.out.println( "同じ" );
} else {
System.out.println( "違う" );
}
a = 5;
b = 4;
b += 1;
System.out.println( "a=" + a + " b=" + b ));
if( a == b ) {
System.out.println( "同じ" );
} else {
System.out.println( "違う" );
}
}
}
実行結果は以下のようになります。
どちらも同じですね!
プリミティブ型の比較をするときに「==」を使って問題はありません。
クラス型の変数は、プリミティブ型と異なり箱のイメージではありません。
データを直接格納するのではなく、メモリのデータの場所を格納してます。そのため一見等しく見えるデータであっても、保管されているメモリの場所が異なると比較をした際に等しくないと扱われます。
では、以下のコードを実行してみましょう。
class Test { public static void main( String[] args ) { String a = "abc"; String b = "abc"; System.out.println( "a=" + a + " b=" + b ); if( a == b ) { System.out.println( "同じ" ); } else { System.out.println( "違う" ); } b = "ab"; b += "c"; System.out.println( "a=" + a + " b=" + b ); if( a == b ) { System.out.println( "同じ" ); } else { System.out.println( "違う" ); } } }実行結果は以下のようになります。
表示上は同じはずなのに、比較すると違うと判断されてしまいます。
これはStringがクラス型であるためです。
2つ目の比較では同じabcのように見えますが、変数bにはabとcを結合したタイミングで新しくメモリ上に作成されたabcの場所を格納しています。
そのため、「aの変数に格納されているabcの場所」と「bの変数に格納されているabcの場所」が異なり、比較した際に「違う」と判定されます。
まとめ
ということで今回は「プリミティブ型とクラス型」についてまとめました。
このようにクラス型の変数は扱う時に注意が必要です。ちなみに配列もクラス型です。そのため配列も注意が秘湯です。
私は配列がクラス型であることを忘れていたため謎の不具合を起こしたことがあります、、、、
次回はこのプリミティブ型を引数にするときの注意などもまとめたいと思います。
今回はこの辺で、ではまた!
コメント
コメントを投稿