Saya mengalami masalah yang menarik (dan sangat membuat frustrasi) dengan equals()
metode hari ini yang menyebabkan apa yang saya pikir sebagai kelas yang teruji rusak dan menyebabkan bug yang membutuhkan waktu sangat lama untuk dilacak.
Hanya untuk kelengkapan, saya tidak menggunakan IDE atau debugger - hanya editor teks kuno dan System.out yang bagus. Waktu sangat terbatas dan itu adalah proyek sekolah.
Bagaimanapun -
Saya sedang mengembangkan keranjang belanja dasar yang bisa mengandung ArrayList
dari Book
objek . Dalam rangka mengimplementasikan addBook()
, removeBook()
dan hasBook()
metode Cart, saya ingin memeriksa apakah Book
sudah ada di Cart
. Jadi saya pergi -
public boolean equals(Book b) {
... // More code here - null checks
if (b.getID() == this.getID()) return true;
else return false;
}
Semua berfungsi dengan baik dalam pengujian. Saya membuat 6 objek dan mengisinya dengan data. Apakah banyak menambahkan, menghapus, memiliki () operasi di Cart
dan semuanya berfungsi dengan baik. Saya membaca bahwa Anda dapat memiliki equals(TYPE var)
atauequals(Object o) { (CAST) var }
tetapi berasumsi bahwa karena itu berfungsi, itu tidak terlalu penting.
Lalu aku berlari ke masalah - saya butuhkan untuk membuat Book
objek dengan hanya satu ID
di dalamnya dari dalam kelas Book. Tidak ada data lain yang akan dimasukkan ke dalamnya. Pada dasarnya sebagai berikut:
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);
}
Tiba-tiba, equals(Book b)
metode ini tidak lagi berfungsi. Ini membutuhkan waktu yang SANGAT lama untuk dilacak tanpa debugger yang bagus dan menganggap Cart
kelas telah diuji dengan benar dan benar. Setelah menunggu equals()
metode sebagai berikut:
public boolean equals(Object o) {
Book b = (Book) o;
... // The rest goes here
}
Segalanya mulai bekerja lagi. Apakah ada alasan metode memutuskan untuk tidak mengambil parameter Book meskipun itu jelas adalah sebuah Book
objek? Satu-satunya perbedaan tampaknya adalah itu dipakai dari dalam kelas yang sama, dan hanya diisi dengan satu anggota data. Saya sangat sangat bingung. Tolong, beri sedikit cahaya?
sumber
Jawaban:
Di Jawa,
equals()
metode yang diwarisi dariObject
adalah:Dengan kata lain, parameter harus bertipe
Object
. Ini disebut overriding ; metode Andapublic boolean equals(Book other)
melakukan apa yang disebut overloading keequals()
metode tersebut.The
ArrayList
penggunaan ditimpaequals()
metode untuk membandingkan isi (misalnya untuk yangcontains()
danequals()
metode), bukan yang kelebihan beban. Dalam sebagian besar kode Anda, menyebut salah satu yang tidak benar menimpaObject
equals 's baik-baik saja, tapi tidak kompatibel denganArrayList
.Jadi, tidak mengganti metode dengan benar dapat menyebabkan masalah.
Saya menimpa sama dengan setiap kali berikut:
Penggunaan
@Override
anotasi dapat membantu satu ton dengan kesalahan konyol.Gunakan setiap kali Anda berpikir Anda menimpa metode kelas super atau antarmuka. Dengan begitu, jika Anda melakukannya dengan cara yang salah, Anda akan mendapatkan kesalahan kompilasi.
sumber
if (!(other instanceof MyClass))return false;
kembalifalse
jikaMyClass
memperluas kelas lain. Tapi itu tidak akan kembalifalse
jika kelas lainnya diperpanjangMyClass
. Bukankah seharusnya tidakequal
terlalu kontradiktif?Jika Anda menggunakan gerhana hanya pergi ke menu atas
sumber
Sedikit di luar topik untuk pertanyaan Anda, tetapi mungkin perlu disebutkan:
Commons Lang telah mendapatkan beberapa metode luar biasa yang dapat Anda gunakan dalam mengesampingkan equals dan hashcode. Lihat EqualsBuilder.reflectionEquals (...) dan HashCodeBuilder.reflectionHashCode (...) . Menyelamatkan saya banyak sakit kepala di masa lalu - walaupun tentu saja jika Anda hanya ingin melakukan "sama dengan" pada ID itu mungkin tidak sesuai dengan keadaan Anda.
Saya juga setuju bahwa Anda harus menggunakan
@Override
anotasi setiap kali Anda mengganti sama dengan (atau metode lainnya).sumber
right click -> source -> generate hashCode() and equals()
,Solusi cepat lain yang menyimpan kode boilerplate adalah anotasi Lombok EqualsAndHashCode . Mudah, elegan, dan dapat disesuaikan. Dan tidak tergantung pada IDE . Sebagai contoh;
Lihat opsi yang tersedia untuk menyesuaikan bidang mana yang akan digunakan dalam persamaan. Lombok tersedia di pakar . Cukup tambahkan dengan cakupan yang disediakan :
sumber
di Android Studio alt + masukkan ---> sama dengan dan kode hash
Contoh:
sumber
Mempertimbangkan:
sumber
obj
dinyatakan sebagaiObject
. Titik warisan adalah bahwa Anda kemudian dapat menetapkanBook
untukobj
. Setelah itu, kecuali Anda menyarankan agarObject
tidak boleh dibandingkan denganString
viaequals()
, kode ini harus sah dan dikembalikanfalse
.yang
instanceOf
pernyataan sering digunakan dalam pelaksanaan equals.Ini adalah perangkap yang populer!
Masalahnya adalah bahwa menggunakan
instanceOf
melanggar aturan simetri:(object1.equals(object2) == true)
jika dan hanya jika(object2.equals(object1))
jika equals pertama benar, dan object2 adalah turunan dari subclass dari kelas di mana obj1 milik, maka equals kedua akan kembali salah!
jika kelas dianggap di mana ob1 milik dinyatakan sebagai final, maka masalah ini tidak dapat muncul, tetapi secara umum, Anda harus menguji sebagai berikut:
this.getClass() != otherObject.getClass();
jika tidak, kembalikan salah, jika tidak, uji bidang untuk membandingkan kesetaraan!sumber
equals()
metode. Dia merekomendasikan untuk tidak menggunakangetClass()
. Alasan utama adalah bahwa hal itu melanggar Prinsip Substitusi Liskov untuk subclass yang tidak mempengaruhi kesetaraan.recordId adalah properti dari objek
sumber