Kilian Foth'un cevabı mükemmel. Bunun neden bir sorun olduğuna ilişkin kanonik bir örnek * eklemek isterim. Bir tamsayı Point sınıfı düşünün:
class Point2D {
public int x;
public int y;
// constructor
public Point2D(int theX, int theY) { x = theX; y = theY; }
public int hashCode() { return x + y; }
public boolean equals(Object o) {
if (this == o) { return true; }
if ( !(o instanceof Point2D) ) { return false; }
Point2D that = (Point2D) o;
return (x == that.x) &&
(y == that.y);
}
}
Şimdi onu 3B nokta olarak sınıflandıralım.
class Point3D extends Point2D {
public int z;
// constructor
public Point3D(int theX, int theY, int theZ) {
super(x, y); z = theZ;
}
public int hashCode() { return super.hashCode() + z; }
public boolean equals(Object o) {
if (this == o) { return true; }
if ( !(o instanceof Point3D) ) { return false; }
Point3D that = (Point3D) o;
return super.equals(that) &&
(z == that.z);
}
}
Süper basit! Puanlarımızı kullanalım:
Point2D p2a = new Point2D(3, 5);
Point2D p2b = new Point2D(3, 5);
Point2D p2c = new Point2D(3, 7);
p2a.equals(p2b); // true
p2b.equals(p2a); // true
p2a.equals(p2c); // false
Point3D p3a = new Point3D(3, 5, 7);
Point3D p3b = new Point3D(3, 5, 7);
Point3D p3c = new Point3D(3, 7, 11);
p3a.equals(p3b); // true
p3b.equals(p3a); // true
p3a.equals(p3c); // false
Muhtemelen neden bu kadar kolay bir örnek gönderdiğimi merak ediyorsundur. İşte yakalamak:
p2a.equals(p3a); // true
p3a.equals(p2a); // FALSE!
2B noktasını eşdeğer 3B noktayla karşılaştırdığımızda, gerçek oluyoruz, ancak karşılaştırmayı tersine çevirdiğimizde, yanlış yapıyoruz (çünkü p2a başarısız oluyor instanceof Point3D
).
Sonuç
Bir yöntemi, bir alt sınıfta, süper sınıfın çalışmasını beklediği zamanla artık uyumlu olmayacak şekilde uygulamak mümkündür.
Eşit () 'i, üst sınıfıyla uyumlu bir şekilde, çok farklı bir alt sınıf üzerine uygulamak genellikle imkansızdır.
İnsanların alt sınıfa izin vermeyi düşündüğünüz bir sınıf yazdığınızda, her yöntemin nasıl davranması gerektiğine dair bir sözleşme yazmak gerçekten iyi bir fikirdir . Daha da iyisi, insanların sözleşmeyi ihlal etmediklerini kanıtlamak için geçersiz kılınan yöntemlerin uygulanmasına karşı koyabilecekleri bir dizi test olacaktır. Neredeyse kimse bunu yapmaz çünkü çok fazla iş var. Ama umursarsan, yapılacak şey budur.
İyi yazılmış bir sözleşmenin harika bir örneği Karşılaştırıcı'dır . .equals()
Yukarıda açıklanan nedenlerden dolayı ne yazdığını görmezden gel . İşte Comparator'un işleri .equals()
yapamayacağının bir örneği .
notlar
Josh Bloch'un "Etkili Java" Öğesi 8, bu örneğin kaynağıydı, ancak Bloch, üçüncü bir eksen yerine bir renk ekleyen ve inç yerine çiftler kullanan bir ColorPoint kullanıyor. Bloch'un Java örneği, örneklerini çevrimiçi ortamda kullanıma sunan Odersky / Spoon / Venners tarafından çoğaltılmıştır .
Birkaç kişi bu örneğe itiraz etti çünkü üst sınıfa alt sınıf hakkında bilgi verirseniz, bu sorunu çözebilirsiniz. Yeterince az sayıda alt sınıf varsa ve eğer ebeveyn onları biliyorsa, bu doğrudur. Ancak asıl soru, başka birinin alt sınıflar yazacağı bir API yapmaktı. Bu durumda, genellikle üst uygulamayı alt sınıflarla uyumlu olacak şekilde güncelleyemezsiniz.
Bonus
Kıyaslayıcı da ilginç çünkü eşittir () düzgün bir şekilde uygulama sorunu etrafında çalışır. Daha da iyisi, bu tür bir miras sorununu çözmek için bir kalıp izler: Strateji tasarım kalıbı. Haskell ve Scala halkının heyecanlandığı Tipeclasses da Strateji kalıbı. Kalıtım kötü ya da yanlış değil, sadece zor. Daha fazla okumak için, Philip Wadler'in makalesine bakın. Geçici polimorfizm nasıl daha az geçici hale getirilir ?