簡體   English   中英

Java等於超級和子類

[英]Java equals super and sub class

我有以下兩個非常簡單的類:

   public class A {

    private int a;

    public A(int a)
    {
    this.a=a;
    }
    public int getA(){
        return a;
    }
    public boolean equals(Object o)
    {
        if (!(o instanceof A))
            return false;
        A other = (A) o;
        return a == other.a;
    }
}

它的子類:

public class B extends A{
    private int b;
    public B(int a, int b)
    {
        super(a);
        this.b = b;
    }
    public boolean equals(Object o)
    {
        if (!(o instanceof B))
            return false;
        B other = (B) o;
        return super.getA() == other.getA() && b == other.b;
    }
}

這看起來似乎是正確的,但在下面的例子中,它違反了Object規范的一般契約的對稱原則,該原則規定:“它是對稱的:對於任何非空引用值x和y,x.equals(y)當且僅當y.equals(x)返回true時,才應返回true。“ http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)失敗的情況如下:

public class EqualsTester {

    public static void main(String[] args) {
        A a = new A(1);
        B b = new B(1,2);
        System.out.println(a.equals(b));
        System.out.println(b.equals(a));
    }

}

第一個返回true,而第二個返回false。 一個似乎正確的解決方案是使用getClass()而不是instanceof 但是,在我們在集合中查找B的情況下,這是不可接受的。 例如:

Set<A> set = new HashSet<A>();
set.add(new A(1));

方法set.contains(new B(1,2)); 將返回false。 這個例子可能不是理想的邏輯可視化,但想象一下如果A是一個Vehicle類而B是一個Car類,其中a是輪子的數量,而字段b是門的數量。 當我們調用contains方法時,我們基本上會問:“我們的套裝是否包含一個四輪車?” 答案應該是肯定的,因為它確實包含它,無論它是汽車還是它的門數。 Joshua Block在Effective Java 2nd Ed第40頁中建議的解決方案是不要讓B繼承自A並且將A的實例作為B中的字段來代替:

public class B {
    private A a;
    private int b;
    public B(int a, int b)
    {
        this.a = new A(a);
        this.b = b;
    }

    public A getAsA()
    {
        return A;
    }  
    public boolean equals(Object o)
    {
        if (!(o instanceof B))
            return false;
        B other = (B) o;
        return a.getA() == other.getA() && b == other.b;
    }
}

但是,這是否意味着我們在需要它的大量情況下失去了使用繼承的權力? 也就是說,當我們擁有具有額外屬性的類時,需要擴展更一般的屬性以重用代碼並只添加其更具體的屬性。

無論收集問題如何,我都會發現b.equals(a)返回true是非常不直觀和令人困惑的。

如果要在某些上下文中考慮ab相等,則明確實現該上下文的相等邏輯。

1)例如,要通過依賴A相等邏輯在HashSet使用AB ,實現一個包含A適配器並實現equals方法:

class MyAdapter {
   private final A a;

   MyAdapter(A a) {
      this.a = a;
   }

   public boolean equals(Object o) {
        if (!(o instanceof MyAdapter)) {
            return false;
        }
        MyAdapter other = (MyAdapter) o;
        return a.getA() == other.a.getA();
    }
}

然后只需將適配器對象添加到集合中:

Set<MyAdapter> set = new HashSet<>();
set.add(new MyAdapter(new A(1)));

然后set.contains(new MyAdapter(new B(1,2))); 返回true

當然,您可以編寫一個包裝類並直接將它傳遞給A (和B s),將MyAdapter隱藏在客戶端代碼中(它可以是包裝類中的private static類),以提高可讀性。

2)標准jdk庫中的一個選項是使用TreeSet

請注意,如果要正確實現Set接口,則由set維護的排序(無論是否提供顯式比較器)必須與equals一致。 (有關與equals一致的精確定義,請參閱ComparableComparator 。)這是因為Set接口是根據equals操作定義的, TreeSet實例使用compareTo (或compare )方法執行所有元素比較,因此從集合的角度來看,通過這種方法被認為相等的元素是相等的。 集合的行為即使其排序與equals不一致也是明確定義的; 它只是不遵守Set接口的一般合同。

因為TreeSet不依賴於equals ,所以只需實現適當的比較器,就像你實現MyAdapter.equals (如果a1.getA() == a2.getA()則返回0 );

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM