Effective Java第40章-@overrideプレゼンテーションを一貫して使用


ユーザ自身が定義したクラスをデータ型とするSetデータ構造を用いる場合,equals,hashcodeなどを正しく再定義して重複をチェックする必要がある.(Override)
このセクションでは、equalsを再定義するときに「再定義」ではなく「マルチ定義」を行うことが主なエラーです.
まず,以下のコードはaからzまでのforループを10回実行し,Set資料構造に追加する.
Setは反復を許さないためequalsを再定義し,漏れはなく,以下の性質を守るためhashCodeも再定義した.
  • の2つのオブジェクトがequalsによって同じと判定された場合、hashCodeも同じであるべきである.
  • しかし、このドメインは成立せず、2つのオブジェクトがequalsによって異なると判定されてもhashCodeは同じである可能性がある.
  • 主にSet,HashMapなどの資料構造では,HashCodeを用いてパケットを決定し,equals比較値を用いて挿入したい要素が既に存在するかどうかを調べる.
    上記の性質に従えば、以下のコードは26を出力すべきである.しかし、次のコードは実際に260の結果を出力する.
    package com.example.Bigram;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class Bigram {
    	private final char first;
    	private final char second;
    
    	public Bigram(char first, char second) {
    		this.first = first;
    		this.second = second;
    	}
    	
    	public boolean equals(Bigram b) {
    		return b.first == first && b.second == second;
    	}
    
    	public int hashCode() {
    		return 31 * first + second;
    	}
    
    	public static void main(String[] args) {
    		Set<Bigram> s = new HashSet<>();
    		for (int i = 0; i < 10; i++) {
    			for (char ch = 'a'; ch <= 'z'; ch++) {
    				s.add(new Bigram(ch, ch));
    			}
    		}
    		System.out.println("크기는 26으로 예상됩니다. 실제 크기 : " + s.size());
    	}
    }
    これはequalsメソッドのパラメータが親クラスでObjectであるためである.

    上図はSetクラスでequalsとhashCodeを宣言する部分です.
    正しく修正されたコードは以下の通りです.
    package com.example.Bigram;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class Bigram {
    	private final char first;
    	private final char second;
    
    	public Bigram(char first, char second) {
    		this.first = first;
    		this.second = second;
    	}
    	
    	public boolean equals(Bigram b) {
    		return b.first == first && b.second == second;
    	}
    	
    	@Override
    	public boolean equals(Object o) {
    		if(!(o instanceof Bigram))
    			return false;
    		Bigram b = (Bigram) o;
    		return b.first == first && b.second == second;
    	}
    
    	public int hashCode() {
    		return 31 * first + second;
    	}
    
    	public static void main(String[] args) {
    		Set<Bigram> s = new HashSet<>();
    		for (int i = 0; i < 10; i++) {
    			for (char ch = 'a'; ch <= 'z'; ch++) {
    				s.add(new Bigram(ch, ch));
    			}
    		}
    		System.out.println("크기는 26으로 예상됩니다. 실제 크기 : " + s.size());
    	}
    }
    @Overrideコメントを貼り付け、パラメータをObjectに変更します.
    @Overrideをペーストしても、パラメータを変更しないとコンパイルできません.
    @Overrideは追加されていませんが、コードの整合性と再定義の事実を明確に残しておくとよいでしょう.
    コアの整理
    すべての再定義メソッドに@Overrideコメントを意識して追加すると、コンパイラはエラーが発生したときにすぐに通知します.例外は一つしかありません.特定のクラスで親の抽象メソッドを再定義した場合は、このコメントを追加する必要はありません(有害ではありません).