Java equals () yöntemini geçersiz kılma - çalışmıyor?


150

equals()Bugün yöntem ile ilginç (ve çok sinir bozucu) bir sorunla karşılaştım .

Tam olarak söylemek gerekirse, bir IDE veya hata ayıklayıcı kullanmıyordum - sadece eski moda metin editörü ve System.out'ları. Zaman çok sınırlıydı ve bir okul projesiydi.

Her neyse -

Bir içeren temel bir alışveriş sepeti geliştirmek ArrayListait Booknesneler . Sepetin , ve yöntemlerini uygulamak için addBook(), içinde zaten var olup olmadığını kontrol etmek istedim . Öyleyse gidiyorum -removeBook()hasBook()BookCart

public boolean equals(Book b) {
    ... // More code here - null checks
    if (b.getID() == this.getID()) return true;
    else return false;
}

Tüm testlerde iyi çalışıyor. 6 nesne oluşturup veri ile dolduruyorum. Üzerinde birçok ekleme, kaldırma, () işlemi yapar Cartve her şey yolunda gider. Sana ki okumak ya sahip equals(TYPE var)yaequals(Object o) { (CAST) var } ancak çalışmasını edilmiş olduğu için, çok fazla önemli değildi farz.

Sonra bir sorunla karşılaştık - Bir oluşturmak için gerekli Bookolan nesneyi sadeceID defteri sınıf içinde onun içinde. Başka veri girilmez. Temel olarak aşağıdakiler:

public boolean hasBook(int i) {
    Book b = new Book(i);
    return hasBook(b);
}

public boolean hasBook(Book b) {
    // .. more code here
    return this.books.contains(b);
}

Birdenbire, equals(Book b)yöntem artık çalışmıyor. Bu çok iyi bir hata ayıklayıcı olmadan izlemek için çok uzun zaman aldı ve Cartsınıf düzgün test ve doğru varsayalım. Swaapping sonra equals()aşağıdaki yöntemi:

public boolean equals(Object o) {
    Book b = (Book) o;
    ... // The rest goes here   
}

Her şey tekrar çalışmaya başladı. Yöntem açıkça defteri parametre olsa almaya karar bir nedeni var mı idi bir Booknesne? Tek fark, aynı sınıftan başlatılmış ve sadece bir veri üyesiyle doldurulmuş gibi görünüyordu. Çok kafam karıştı. Lütfen, biraz ışık tuttun mu?


1
Yansıtıcı olarak eşit yöntemleri geçersiz kılma konusunda 'Sözleşme'yi ihlal ettiğimin farkındayım - ancak nesnenin jenerikler kullanmadan ArrayList'te var olup olmadığını kontrol etmenin hızlı bir yoluna ihtiyacım vardı.
Josh Smeaton

1
Bu Java hakkında bilgi ve eşittir
jjnguy

Yanıtlar:


329

Java'da, equals()devralınan yöntem Objectşöyledir:

public boolean equals(Object other);

Başka bir deyişle, parametre türünde olmalıdır Object. Buna geçersiz kılma denir ; senin yöntem public boolean equals(Book other)denen yapar aşırı etmek equals()yöntemle.

Kullanımlar , aşırı yüklenmiş olanları değil , içeriği karşılaştırmak ArrayListiçin geçersiz kılınan equals()yöntemler kullanır (örneğin, kendi yöntemleri contains()ve equals()yöntemleri) . Kodunuzun çoğunda, eşit değerlerini geçersiz kılmayan kodu çağırmak iyi, ancak uyumlu değil .ObjectArrayList

Bu nedenle, yöntemi doğru şekilde geçersiz kılmamak sorunlara neden olabilir.

Ben geçersiz kılma aşağıdaki her zaman eşittir:

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MyClass)) return false;
    MyClass otherMyClass = (MyClass)other;
    ...test other properties here...
}

@OverrideEk açıklamanın kullanımı aptalca hatalarla bir tona yardımcı olabilir.

Bir süper sınıfın veya arayüzün yöntemini geçersiz kıldığınızı düşündüğünüzde kullanın. Bu şekilde, yanlış şekilde yaparsanız, derleme hatası alırsınız.


31
Bu, @Override ek açıklaması lehine iyi bir argüman ... OP @Override kullansaydı derleyici ona aslında bir ana sınıf yöntemini geçersiz kılmadığını söylerdi ...
Cowan

1
@Override asla farkında değildi, bunun için teşekkürler! Ayrıca geçersiz kılma hashCode () gerçekten yapılması gerektiğini ve hatayı daha erken fark etmiş olabilir eklemek istiyorum.
Josh Smeaton

5
Bazı IDE'ler (örneğin Eclipse) sınıf üyesi değişkenlerine göre sizin için equals () ve hashcode () yöntemlerini otomatik olarak oluşturabilir.
sk.

1
if (!(other instanceof MyClass))return false;döner falseeğer MyClassbaşka sınıfını genişletir. Ama falsediğer sınıf genişleseydi geri gelmeyecekti MyClass. Daha equalaz çelişkili olmamalı mı ?
Robert

19
İnstanceof komutunu kullanırken, önceki boş denetim gereksizdir.
Mateusz Dymczyk

108

Tutulma kullanıyorsanız üst menüye gidin

Kaynak -> Eşittir () ve hashCode () üret


Katılıyorum! Daha önce hiç bilmediğim ve onu üreten bu daha az hata eğilimli yapar
Boy

Burada aynı. Teşekkürler Fred!
Anila

16
IntelliJ'de bunu Code → Generate… veya control + N altında bulabilirsiniz. :)
rightfold

Netbeans'te menü çubuğu> Kaynak (veya sağ tıklama)> Kod Ekle (veya Ctrl-I) bölümüne gidin ve Eşittir () oluştur'a tıklayın ...
Solomon

11

Sorunuzla ilgili biraz konu dışı, ancak yine de bahsetmeye değer:

Commons Lang , eşitleri ve hashcode'u geçersiz kılmak için kullanabileceğiniz bazı mükemmel yöntemlere sahiptir. Check out EqualsBuilder.reflectionEquals (...) ve HashCodeBuilder.reflectionHashCode (...) . Geçmişte bana baş ağrısı bol kurtardı - tabii ki sadece kimlik üzerinde "eşit" yapmak istiyorsanız, bu durumunuza uygun olmayabilir.

Ayrıca @Override, eşitlemeyi (veya başka bir yöntemi) geçersiz kıldığınızda ek açıklamayı kullanmanız gerektiğini de kabul ediyorum .


4
Eğer bir tutulma kullanıcısıysan, gidebilirsin right click -> source -> generate hashCode() and equals(),
tunaranch

1
Bu yöntemin çalışma zamanında yürütüldüğü konusunda haklı mıyım? Yansıma nedeniyle başka bir öğeye eşitlik açısından kontrol eden öğelerle büyük bir koleksiyondan geçersek performans sorunları yaşamayacak mıyız?
Gaket

4

Isı yalıtım levhası kodunu kaydeden bir diğer hızlı çözüm, Lombok EqualsAndHashCode ek açıklamasıdır . Kolay, zarif ve özelleştirilebilir. Ve IDE'ye bağlı değildir . Örneğin;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals.
public class ErrorMessage{

    private long        errorNumber;
    private int         numberOfParameters;
    private Level       loggingLevel;
    private String      messageCode;

Eşit alanlarda kullanılacak alanları özelleştirmek için mevcut seçeneklere bakın . Lombok içinde avalaible maven . Sadece sağlanan kapsamla ekleyin :

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.14.8</version>
    <scope>provided</scope>
</dependency>

1

Android Studio'da alt + insert ---> eşittir ve hashCode

Misal:

    @Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Proveedor proveedor = (Proveedor) o;

    return getId() == proveedor.getId();

}

@Override
public int hashCode() {
    return getId();
}

1

Düşünmek:

Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...

1
@Elazar Nasıl? objolarak ilan edilir Object. Miras noktası sonra bir atayabilirsiniz olmasıdır Bookiçin obj. Bundan sonra, a'nın Objectbir Stringyolla karşılaştırılabilir olmamasını önermezseniz equals(), bu kod mükemmel bir şekilde yasal olmalı ve geri dönmelidir false.
bcsb1001

Tam olarak öneririm. Oldukça yaygın olarak kabul edildiğine inanıyorum.
Elazar

0

instanceOfifadesi genellikle eşittir uygulanmasında kullanılır.

Bu popüler bir tuzak!

Sorun şu ki instanceOf, kullanım simetri kuralını ihlal ediyor:

(object1.equals(object2) == true) ancak ve ancak (object2.equals(object1))

ilk eşittir true ise ve object2, obj1'in ait olduğu sınıfın bir alt sınıfının bir örneğiyse, ikinci eşittir false değerini döndürür!

ob1'in ait olduğu kabul edilen sınıf nihai olarak ilan edilirse, bu sorun ortaya çıkamaz, ancak genel olarak aşağıdaki gibi test etmelisiniz:

this.getClass() != otherObject.getClass(); değilse, yanlış döndür, aksi takdirde eşitliği karşılaştırmak için alanları test et!


3
Yöntemin geçersiz kılınmasıyla ilgili sorunları tartışan büyük bir bölüm olan Bloch, Etkili Java, Öğe 8'e bakın equals(). Kullanmaya karşı önermektedir getClass(). Bunun ana nedeni, eşitliği etkilemeyen alt sınıflar için Liskov İkame Prensibi'ni ihlal etmesidir.
Stuart Marks

-1

recordId, nesnenin özelliğidir

@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Nai_record other = (Nai_record) obj;
        if (recordId == null) {
            if (other.recordId != null)
                return false;
        } else if (!recordId.equals(other.recordId))
            return false;
        return true;
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.