== はなぜ言語によって「参照」と「値」のどちらを比較するのか
==(や is)で比較したのに、中身が同じはずの値がfalseになる——この種の間違いは、原因が「参照比較か値比較か」という1点に集約されるにもかかわらず、言語によって発生条件も直し方もばらばらです。5つの言語で同じ罠を横に並べます。
言語ごとの現れ方
Java: == と .equals() の違い(文字列比較)
1 public class Main { 2 public static void main(String[] args) { 3 String a = new String("java"); 4 String b = new String("java"); 5 System.out.println(a == b ); ^ 6 } 7 }
出力: false期待: true (コンパイルは通っています)
newで作った文字列同士は中身が同じでも==はfalseになります。文字列リテラル同士だけは定数プールの共有で偶然trueになるため、余計に誤解を招きます。
Python: is と == の違い(小さい整数キャッシュで「たまたま動く」)
1 a = int("1000") 2 b = int("1000") 3 print(a is b) ^
出力: False期待: True (エラーにはなりません)
isは同一性、==は値の比較です。-5〜256の小さい整数はキャッシュされているため、本来isを使うべきでない場面でもisで「たまたま」正しく動いてしまいます。
C#: == が参照を比較してしまう理由
1 class Point { public int X; public int Y; } 2 class Program { 3 static void Main(string[] args) { 4 Point a = new Point { X = 1, Y = 2 }; 5 Point b = new Point { X = 1, Y = 2 }; ^ 6 System.Console.WriteLine(a == b); 7 } 8 }
出力: False
classの==は既定で参照比較です。同じフィールド値を持つ2つのインスタンスもfalseになります(structやrecordは挙動が異なります)。
JavaScript: 同じ内容のオブジェクト同士が === でfalseになる理由
1 const a = { id: 1 }; 2 const b = { id: 1 }; 3 console.log(a === b ); ^
出力: false期待: true (エラーにはなりません)
オブジェクト・配列の==・===はどちらも参照比較です。中身のプロパティを比較するには自分でフィールドを取り出して比較する必要があります。
C言語: error: invalid operands to binary ==(構造体同士の==比較)
1 #include <stdio.h> 2 3 struct Point { int x, y; }; 4 5 int main(void) { 6 struct Point a = {1, 2}; 7 struct Point b = {1, 2}; 8 if (a == b ) { ^ 9 printf("same\n"); 10 } 11 return 0; 12 }
main.c:8:11: error: invalid operands to binary == (have 'struct Point' and 'struct Point')
構造体には==自体が定義されておらず、コンパイルエラーになります。「比較できない」ことが最初から明示される点が、他の4言語(黙って参照を比較する)と対照的です。
まとめ
共通しているのは「変数が指しているのは値そのものではなく、値の置き場所(参照)である」という一点です。プリミティブ型・小さい整数・文字列リテラルなど、言語ごとに用意された「例外的に値比較に見えるケース」に一度でも当たると、この誤解はかえって強化されます。迷ったら、その言語の「値そのものを比較する方法」(.equals() / == / 各フィールドの比較など)を必ず確認してください。