Dapatkah seseorang menjelaskan kepada saya secara sederhana, mengapa kode ini memberikan pengecualian, "Metode perbandingan melanggar kontrak umum!", Dan bagaimana cara memperbaikinya?
private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
java
comparator
n00bster
sumber
sumber
s1.getParent().equals(s2)
bukans1.getParent() == s2
.s1
orang tuas2
, dans2
bukan orang tua daris1
. KemudiancompareParents(s1, s2)
adalah0
, tapicompareParents(s2, s1)
adalah1
. Itu tidak masuk akal. (Selain itu, ini bukan transitif, seperti aix yang disebutkan di bawah ini.)Jawaban:
Komparator Anda tidak transitif.
Biarkan
A
menjadi orang tua dariB
, danB
menjadi orang tua dariC
. SejakA > B
danB > C
, maka harus demikianA > C
. Namun, jika pembanding Anda diaktifkanA
danC
, itu akan mengembalikan nol, artinyaA == C
. Ini melanggar kontrak dan karenanya melempar pengecualian.Itu agak baik dari perpustakaan untuk mendeteksi ini dan memberi tahu Anda, daripada berperilaku tidak menentu.
Salah satu cara untuk memenuhi persyaratan transitivitas
compareParents()
adalah dengan melintasigetParent()
rantai alih-alih hanya melihat leluhur langsung.sumber
java.util.Arrays.sort
stackoverflow.com/questions/7849539/…Hanya karena ini yang saya dapatkan ketika saya mencari kesalahan ini di Google, masalah saya adalah yang saya miliki
yang
value >= other.value
harus (jelas) benar-benar menjadivalue > other.value
sehingga Anda dapat benar-benar kembali 0 dengan objek yang sama.sumber
value
adalah NaN (jikavalue
adalahdouble
ataufloat
), itu akan gagal juga.Pelanggaran kontrak seringkali berarti bahwa pembanding tidak memberikan nilai yang benar atau konsisten ketika membandingkan objek. Misalnya, Anda mungkin ingin melakukan perbandingan string dan memaksa string kosong untuk mengurutkan sampai akhir dengan:
Tapi ini mengabaikan kasus di mana KEDUA satu dan dua kosong - dan dalam kasus itu, nilai yang salah dikembalikan (1 bukannya 0 untuk menunjukkan kecocokan), dan pembanding melaporkan itu sebagai pelanggaran. Seharusnya ditulis sebagai:
sumber
Bahkan jika compareTo Anda memegang transitivitas dalam teori, kadang-kadang bug halus mengacaukan segalanya ... seperti kesalahan aritmatika floating point. Itu terjadi pada saya. ini kode saya:
Properti transitif jelas berlaku, tetapi untuk beberapa alasan saya mendapatkan IllegalArgumentException. Dan ternyata karena kesalahan kecil dalam aritmatika floating point, kesalahan pembulatan di mana menyebabkan properti transitif pecah di mana mereka seharusnya tidak! Jadi saya menulis ulang kode untuk mempertimbangkan perbedaan yang sangat kecil 0, dan berhasil:
sumber
Dalam kasus kami mendapatkan kesalahan ini karena kami tidak sengaja membalik urutan perbandingan s1 dan s2. Jadi hati-hati untuk itu. Itu jelas jauh lebih rumit daripada yang berikut tetapi ini adalah ilustrasi:
sumber
Java tidak memeriksa konsistensi dalam arti yang ketat, hanya memberi tahu Anda jika mengalami masalah serius. Juga tidak memberi Anda banyak informasi dari kesalahan.
Saya bingung dengan apa yang terjadi di sorter saya dan membuat konsistensi yang ketatChecker, mungkin ini akan membantu Anda:
sumber
Compare
,Convert
(dan berpotensi lainnya) tidak didefinisikan. Harap perbarui sniplet kode dengan contoh lengkap.checkConsi(s)tency
dan menghapus semua@param
deklarasi berlebihan untuk membuat kode lebih mudah dibaca.Dalam kasus saya, saya melakukan sesuatu seperti berikut:
Yang saya lupa periksa adalah ketika a.someField dan b.someField keduanya nol.
sumber
Saya telah melihat ini terjadi dalam sepotong kode di mana pemeriksaan berulang untuk nilai null dilakukan:
sumber
Jika
compareParents(s1, s2) == -1
kemudiancompareParents(s2, s1) == 1
diharapkan. Dengan kode Anda itu tidak selalu benar.Khususnya jika
s1.getParent() == s2 && s2.getParent() == s1
. Itu hanya salah satu masalah yang mungkin terjadi.sumber
Mengedit Konfigurasi VM berfungsi untuk saya.
sumber
-
a awal solusi yang diusulkan. Mungkin Anda bermaksud sesuatu seperti daftar poin-poin satu item saja.Anda tidak dapat membandingkan data objek seperti ini:
s1.getParent() == s2
- ini akan membandingkan referensi objek. Anda harus menggantiequals function
untuk kelas Foo dan kemudian membandingkannya seperti inis1.getParent().equals(s2)
sumber