Java | オブジェクト指向:equals と == の違い

Java Java
スポンサーリンク

ざっくり結論

== は「同じ実体(同じ場所)か」を比べる演算子、equals は「中身(値)が同じか」を比べるメソッドです。プリミティブでは == が値比較、参照型(オブジェクト)では == が参照比較(同一性)、equals は値比較(等価性)になります。どちらを使うかは「何を同じとみなしたいか」で決まります。


プリミティブと参照型での違い

プリミティブ(int, double, boolean など)

プリミティブは「値そのもの」なので、== は値の比較です。equals はありません。

int a = 10, b = 10, c = 20;
System.out.println(a == b); // true(値が同じ)
System.out.println(a == c); // false
Java

参照型(String, 自作クラスなど)

  • == は「同じインスタンス(同じ参照)か」を比較します。
  • equals は「値や意味が同じか」を比較します(クラスの実装次第)。
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);      // false(別インスタンス)
System.out.println(s1.equals(s2)); // true(文字列内容が同じ)
Java

equals の設計意図(重要ポイントの深掘り)

equals は「何をもって同じとするか」を型に刻む

値オブジェクトでは equals をオーバーライドして、等価の定義(比較に使うフィールド)を明確にします。HashSet/HashMap で正しく扱うには equals と hashCode を同じ基準で実装することが必須です。

import java.util.Objects;

public final class Email {
    private final String value;
    public Email(String raw) {
        var v = raw == null ? "" : raw.trim().toLowerCase();
        if (!v.contains("@")) throw new IllegalArgumentException();
        this.value = v;
    }
    @Override public boolean equals(Object o) {
        return o instanceof Email e && Objects.equals(value, e.value);
    }
    @Override public int hashCode() { return Objects.hash(value); }
}
Java

equals を用意すると、「中身が同じなら同じ」と安全に言えるようになります。== は「同じインスタンスか」しか分かりません。


よくある混乱ポイントと注意例

String の == と equals

String の equals は内容比較、== は参照比較。リテラルは「文字列プール」で同一参照になることがあり、誤って true になるケースがあるため、内容比較は常に equals を使います。

String a = "abc";
String b = "abc";
System.out.println(a == b);      // true(同一プール参照になることが多い)
System.out.println(a.equals(b)); // 常に内容比較(こちらを使う)
Java

ラッパー型(Integer など)の == と equals

オートボクシングやキャッシュ(例えば Integer は -128〜127 をキャッシュ)により、== が期待通りに動かないことがあります。値比較は equals を使うのが安全です。

Integer x = 127, y = 127;
System.out.println(x == y);        // true(キャッシュ参照が同じ)
Integer p = 128, q = 128;
System.out.println(p == q);        // false になり得る(別インスタンス)
System.out.println(p.equals(q));   // true(値比較)
Java

BigDecimal の equals と compareTo

BigDecimal はスケールも含めて equals 比較します。値の大小だけ比較したいなら compareTo を使います。

var a = new java.math.BigDecimal("1.0");
var b = new java.math.BigDecimal("1.00");
System.out.println(a.equals(b));     // false(スケール違い)
System.out.println(a.compareTo(b));  // 0(数値として等しい)
Java

配列は equals が参照比較

配列の equals は Object の実装を継承し、参照比較になります。内容比較には Arrays.equals/Arrays.deepEquals を使います。

int[] u = {1,2}, v = {1,2};
System.out.println(u.equals(v));                      // false(参照比較)
System.out.println(java.util.Arrays.equals(u, v));    // true(内容比較)
Java

== を使うべき場面と equals を使うべき場面

== が適切な場面

  • プリミティブの比較
  • 参照が同一かどうか(同じインスタンスか)の判定
  • enum(同一インスタンスが保証される)では == で等価判定してよい
enum Color { RED, BLUE }
Color c1 = Color.RED, c2 = Color.RED;
System.out.println(c1 == c2); // true(enumはインスタンスが一意)
Java

equals が適切な場面

  • 参照型の値比較(文字列、値オブジェクト、ラッパー型など)
  • コレクションの contains、Map のキー比較など「等価性」を前提とする操作
var list = java.util.List.of("a", "b");
System.out.println(list.contains(new String("a"))); // true(equalsで内容比較)
Java

null と安全な書き方

NullPointerException を避ける equals の書き方

呼び出し側が null の可能性があるときは、定数側から equals を呼ぶ、または Objects.equals を使うと安全です。

String s = null;
System.out.println("x".equals(s));                    // false(安全)
System.out.println(java.util.Objects.equals(s, "x")); // false(安全)
Java

例題で理解を固める

例 1: 値オブジェクトの比較

Email e1 = new Email("A@b.com");
Email e2 = new Email("a@B.COM");      // 正規化で同値になる設計
System.out.println(e1 == e2);         // false(別インスタンス)
System.out.println(e1.equals(e2));    // true(値が同じ)
Java

例 2: 参照同一性が必要な判定

class Session { /* ... */ }
Session s1 = new Session();
Session s2 = s1;
System.out.println(s1 == s2);         // true(同じセッション実体)
Java

仕上げのアドバイス(重要部分のまとめ)

  • プリミティブは == で値比較、参照型は「同一性なら ==」「等価性なら equals」。
  • 文字列やラッパー型の値比較は必ず equals。配列の内容比較は Arrays.equals。
  • 値オブジェクトを設計するときは equals/hashCode をセットで実装し、「何を同じとするか」を型に刻む。
  • null 安全のために定数側から equals を呼ぶか、Objects.equals を使う。

タイトルとURLをコピーしました