.Equals () oluştururken instanceof yerine getClass () 'i tercih etmek için herhangi bir neden var mı?


174

Eclipse oluşturmak .equals()ve kullanıyorum ve .hashCode()"Türleri karşılaştırmak için 'instanceof' kullanın" etiketli bir seçenek var. Varsayılan, bu seçeneğin işaretinin kaldırılması ve .getClass()türleri karşılaştırmak için kullanılmasıdır. Ben tercih için bir neden var mı .getClass()üzerinde instanceof?

Kullanmadan instanceof:

if (obj == null)
  return false;
if (getClass() != obj.getClass())
  return false;

Kullanma instanceof:

if (obj == null)
  return false;
if (!(obj instanceof MyClass))
  return false;

Genellikle instanceofseçeneği işaretlerim ve sonra girip " if (obj == null)" işaretini kaldırırım . (Boş nesneler her zaman başarısız olacağından gereksizdir instanceof.) Bunun kötü bir fikir olmasının bir nedeni var mı?


1
İfadesi x instanceof SomeClassise yanlıştır xolduğunu null. Bu nedenle, ikinci sözdiziminin null kontrole ihtiyacı yoktur.
rds

5
@rds Evet, kod pasajından hemen sonra gelen paragraf bunu da söylüyor. Kod snippet'inde çünkü Eclipse bunu üretir.
Kip

Yanıtlar:


100

Eğer kullanırsanız instanceof, sizin yapma equalsuygulaması finalyönteminin simetri sözleşme koruyacaktır: x.equals(y) == y.equals(x). Eğer finalkısıtlayıcı görünüyor dikkatle geçersiz uygulamaları tamamen tarafından kurulan sözleşme korumak emin olmak için nesne denklik anlayışına incelemek Objectsınıfta.


tam da yukarıdaki josh bloch alıntısını okuduğumda aklıma geldi. +1 :)
Johannes Schaub - litb

13
kesinlikle anahtar karar. simetri uygulanmalıdır ve instanceof yanlışlıkla asimetrik olmayı çok kolaylaştırır
Scott Stanchfield

Ben de "eğer eklersiniz final(hem kısıtlayıcı görünmektedir" equalsve hashCodeardından kullanın) getClass()eşitliği yerine instanceofsimetrisini ve geçişlilik gereksinimlerini korumak için equalssözleşme.
Shadow Man

176

Josh Bloch yaklaşımınızı destekliyor:

instanceofYaklaşımı desteklememin sebebi, yaklaşımı kullandığınızda, getClassnesnelerin aynı sınıftaki, aynı çalışma zamanı türündeki diğer nesnelere eşit olma kısıtlamasına sahip olmanızdır. Bir sınıfı genişletirseniz ve ona birkaç zararsız yöntem eklerseniz, alt sınıftaki bazı nesnelerin süper sınıfın bir nesnesine eşit olup olmadığını kontrol edin, nesneler tüm önemli yönlerde eşit olsa bile, Eşit olmamaları şaşırtıcı bir cevap. Aslında, bu Liskov ikame prensibinin sıkı bir yorumunu ihlal eder ve çok şaşırtıcı davranışlara yol açabilir. Java'da, özellikle koleksiyonların çoğu (HashTablevb.) eşittir yöntemine dayanır. Süper sınıfın bir üyesini bir karma tablosuna anahtar olarak koyarsanız ve ardından bir alt sınıf örneği kullanarak ararsanız, bulamazsınız, çünkü bunlar eşit değildir.

Ayrıca bu SO cevabına bakınız .

Etkili Java bölüm 3 de bunu kapsamaktadır.


18
+1 Java yapan herkes bu kitabı en az 10 kez okumalıdır!
André

16
İnstanceof yaklaşımıyla ilgili sorun, Object.equals () öğesinin "simetrik" özelliğini bozmasıdır, çünkü x.equals (y) == true, ancak x.getClass ise y.equals (x) == false ()! = y.getClass (). Kesinlikle gerekli olmadığı sürece bu özelliği kırmamayı tercih ederim (yani yönetilen veya proxy nesneleri için geçersiz kılmaya eşittir ()).
Kevin Sitze

8
İnstanceof yaklaşımı, ancak alt sınıf, alt sınıf nesneleri arasındaki eşitliğin ne zaman olması gerektiğini tanımladığı zaman ve ancak uygun olduğunda uygundur. Kullanmak getClassLSP'yi ihlal etmez, çünkü LSP yalnızca mevcut örneklerle yapılabilecekler ile ilgilidir - ne tür örneklerin oluşturulabileceği ile değil. Döndürülen sınıf getClass, bir nesne örneğinin değişmez bir özelliğidir. LSP, bu özelliğin onu oluşturan sınıftan başka bir sınıfı gösterdiği bir alt sınıf oluşturmanın mümkün olması gerektiği anlamına gelmez.
supercat

66

Eşit Angels Langers Sırları, Josh Bloch ve Barbara Liskov'un da aralarında bulunduğu birkaç yaygın ve iyi bilinen örnek için uzun ve ayrıntılı bir tartışma ile ilgilenir ve bunların çoğunda birkaç sorun keşfeder. Ayrıca instanceofvs alır getClass. Ondan bazı alıntılar

Sonuçlar

Eşit seçilmiş dört (eşit) uygulama örneğini inceledikten sonra, neyi sonuçlandırıyoruz?

Her şeyden önce: equals () uygulamasında tip eşleşmesi için denetim yapmanın iki farklı yolu vardır. Bir sınıf, instanceof operatörü aracılığıyla üst ve alt sınıf nesneleri arasında karışık tipte karşılaştırmaya izin verebilir veya bir sınıf, getClass () testi aracılığıyla farklı türdeki nesnelere eşit olmayan olarak davranabilir. Yukarıdaki örnekler getClass () kullanarak equals () uygulamalarının instanceof kullanan uygulamalardan genellikle daha sağlam olduğunu güzel bir şekilde göstermiştir.

İnstanceof testi yalnızca son sınıflar için veya bir üst sınıfta en azından yöntem eşittir () sonsa doğrudur. İkincisi, temelde hiçbir alt sınıfın üst sınıfın durumunu genişletmemesi gerektiği anlamına gelir, ancak yalnızca geçici veya statik alanlar gibi nesnenin durumu ve davranışı için alakasız işlevsellik veya alanlar ekleyebilir.

Diğer yandan getClass () testini kullanan uygulamalar her zaman equals () sözleşmesine uygundur; doğru ve sağlamdırlar. Ancak semantik olarak, test örneğini kullanan uygulamalardan anlamsal olarak çok farklıdırlar. GetClass () kullanan uygulamalar, alt sınıf herhangi bir alan eklemese ve eşittir () öğesini geçersiz kılmak istemese bile, alt sınıf üst sınıf nesnelerle karşılaştırılmasına izin vermez. Böyle bir "önemsiz" sınıf uzantısı, örneğin tam olarak bu "önemsiz" amaç için tanımlanan bir alt sınıfa bir hata ayıklama-yazdırma yönteminin eklenmesi olacaktır. Üst sınıf, getClass () denetimi aracılığıyla karışık tür karşılaştırmasını yasaklarsa, önemsiz uzantı üst sınıfıyla karşılaştırılamaz. Bunun bir sorun olup olmadığı tamamen sınıfın anlambilimine ve uzantının amacına bağlıdır.


61

Bunun nedeni sözleşmenin getClasssimetrik özelliğini sağlamaktır equals. Eşittir 'JavaDocs:

Simetriktir: x ve y null olmayan referans değerleri için x.equals (y), yalnızca y.equals (x) true değerini döndürürse true değerini döndürmelidir.

İnstanceof kullanarak simetrik olmamak mümkündür. Örneği düşünün: Köpek, Animal'i uzatır. Animal's equalsAnimal'in instanceofkontrolünü yapar . Köpek adlı equalsbir yapar instanceofKöpek çek. Hayvan a ve Köpek d verin (diğer alanlarla aynı):

a.equals(d) --> true
d.equals(a) --> false

Bu simetrik özelliği ihlal eder.

Eşitliğin sözleşmesini sıkı bir şekilde takip etmek için simetri sağlanmalı ve bu nedenle sınıfın aynı olması gerekir.


8
Bu sorunun ilk açık ve özlü cevabı bu. Kod örneği bin kelimeye bedeldir.
Johnride

Günün kurtardığı tüm diğer ayrıntılı cevaplardan geçiyordum.Bu sorunun basit, özlü, zarif ve en iyi yanıtı.

1
Bahsettiğiniz gibi simetri şartı vardır. Ama aynı zamanda “geçişlilik” şartı da var. Eğer a.equals(c)ve b.equals(c), o zaman a.equals(b)( Dog.equalssadece return super.equals(object)ne zaman yapmanın naif yaklaşımı !(object instanceof Dog)ancak bir Köpek örneği olduğunda fazladan alanları kontrol etmek simetriyi ihlal etmez, ancak geçişliliği ihlal eder)
Shadow Man

Güvende olmak için, bir getClass()eşitlik kontrolü kullanabilirsiniz veya instanceofeğer sizin equalsve hashCodeyöntemlerinizi yaparsanız bir kontrol kullanabilirsiniz final.
Shadow Man

26

Bu dini bir tartışma. Her iki yaklaşımın da sorunları var.

  • Kullanım instanceof ve alt sınıflar için önemli üye eklemek asla.
  • GetClass kullanın ve Liskov değiştirme ilkesini ihlal edersiniz.

Bloch'un Etkili Java Second Edition'da başka bir tavsiyesi var :

  • Madde 17: Kalıtım için tasarım ve belge veya yasaklamak

1
getClassTaban sınıfı eşit olarak karşılaştırılan farklı alt sınıfların örneklerini oluşturmanın mümkün olduğu bir yöntemi belgelemedikçe, kullanımla ilgili hiçbir şey LSP'yi ihlal etmeyecektir. Hangi LSP ihlali görüyorsunuz?
supercat

Belki de GetClass Kullan olarak yeniden ifade edilmeli ve alt türleri LSP ihlali tehlikesi ile karşı karşıya bırakmalısınız. Bloch'un Etkili Java'da bir örneği var - burada da tartışıldı . Onun mantığı büyük ölçüde devlet eklemeyen alt tipler uygulayan geliştiriciler için şaşırtıcı bir davranışla sonuçlanabilir. Demek istediğim, dokümantasyon çok önemli.
McDowell

2
Farklı sınıfların iki örneğinin kendilerini bildirmesi gereken tek zaman, eşitliğin ne anlama geldiğini tanımlayan ortak bir temel sınıftan veya arayüzden miras almalarıdır [Java'nın şüphesiz IMHO'nun bazı toplama arayüzlerine uyguladığı bir felsefe]. Temel sınıf sözleşmesi, söz getClass()konusu sınıfın temel sınıfa dönüştürülebilir olmasının ötesinde anlamlı olarak düşünülmemesi gerektiğini belirtmedikçe, getiri, getClass()örneklerin eşit olması için eşleşmesi gereken diğer herhangi bir özellik gibi düşünülmelidir.
supercat

1
@ eyalzba Eşitlik sözleşmesine bir alt sınıf eklenmiş üyeler varsa instanceof_ kullanılırsa, bu simetrik eşitlik gereksinimini ihlal eder . GetClass alt sınıflarını kullanmak hiçbir zaman üst türe eşit olamaz. Zaten geçersiz kılmanız gerekmiyor, ama bunu yaparsanız ödünç almanız gerekir.
McDowell

2
"Miras için tasarım ve belge ya da yasakla" bence çok önemli. Anladığım kadarıyla, eşitliği geçersiz kılmanız gereken tek zaman, değiştirilemez bir değer türü oluşturuyorsanız, bu durumda sınıfın son olarak bildirilmesi gerektiğidir. Kalıtım olmayacağından, eşitleri gerektiği gibi uygulayabilirsiniz. Kalıtıma izin verdiğiniz durumlarda, nesneler referans eşitliğine göre karşılaştırılmalıdır.
TheSecretSquad

23

Yanılıyorsam beni düzeltin, ancak örneğinizin karşılaştırdığınız sınıfın bir alt sınıfı DEĞİL olduğundan emin olmak istediğinizde getClass () yararlı olacaktır. Bu durumda instanceof kullanırsanız bunu bilemezsiniz çünkü:

class A { }

class B extends A { }

Object oA = new A();
Object oB = new B();

oA instanceof A => true
oA instanceof B => false
oB instanceof A => true // <================ HERE
oB instanceof B => true

oA.getClass().equals(A.class) => true
oA.getClass().equals(B.class) => false
oB.getClass().equals(A.class) => false // <===============HERE
oB.getClass().equals(B.class) => true

Benim için sebep bu
Karlihnos

5

Yalnızca bu sınıfın eşleşmesini sağlamak istiyorsanız kullanın getClass() ==. Eğer alt sınıfları eşleştirmek istiyorsanız o instanceofzaman gereklidir.

Ayrıca, instanceof bir null ile eşleşmez, ancak bir null ile karşılaştırmak güvenlidir. Bu yüzden null kontrol etmek zorunda değilsiniz.

if ( ! (obj instanceof MyClass) ) { return false; }

İkinci noktanız: Soruda bundan daha önce bahsetti. "Genellikle instanceof seçeneğini işaretlerim ve sonra girip 'if (obj == null)' onayını kaldırırım."
Michael Myers

5

Belirli bir sınıfın alt sınıfının üst sınıfına eşit olup olmadığını değerlendirirsiniz.

class LastName
{
(...)
}


class FamilyName
extends LastName
{
(..)
}

burada 'instanceof' kullanacağım, çünkü LastName'in FamilyName ile karşılaştırılmasını istiyorum

class Organism
{
}

class Gorilla extends Organism
{
}

burada 'getClass' kullanacağım, çünkü sınıf zaten iki örneğin eşdeğer olmadığını söylüyor.


3

instanceof , aynı sınıf veya alt sınıflarının kurumları için çalışır

Bir nesnenin bir sınıf örneği mi, alt sınıf örneği mi yoksa belirli bir arabirimi uygulayan bir sınıf örneği mi olduğunu sınamak için kullanabilirsiniz.

ArryaList ve RoleList'in her ikisi de Liste örneğidir

Süre

getClass () == o.getClass () yalnızca her iki nesne (this ve o) tam olarak aynı sınıfa aitse geçerli olur.

Yani neyi karşılaştırmanız gerektiğine bağlı olarak birini veya diğerini kullanabilirsiniz.

Eğer mantığınız şöyle ise: "Bir nesne diğerine eşittir ancak ikisi de aynı sınıftaysa", çoğu durumda olduğunu düşündüğüm "eşittir" için gitmelisiniz.


3

Her iki yöntemin de sorunları vardır.

Alt sınıf kimliği değiştirirse, gerçek sınıflarını karşılaştırmanız gerekir. Aksi takdirde, simetrik özelliği ihlal edersiniz. Örneğin Person, aynı ada sahip olsalar bile , farklı s türleri eşdeğer kabul edilmemelidir.

Bununla birlikte, bazı alt sınıflar kimliği değiştirmez ve bunların kullanılması gerekir instanceof. Örneğin, bir grup değiştirilemez nesnemiz varsa Shape, Rectangleuzunluğu ve genişliği 1 olan bir cihaza eşit olmalıdır Square.

Uygulamada, sanırım eski davanın gerçek olması daha olasıdır. Genellikle, alt sınıflandırma kimliğinizin temel bir parçasıdır ve sizin ebeveyniniz gibi olmanız dışında tek bir şey yapabilmeniz sizi eşit yapmaz.


-1

Aslında bir nesnenin bir hiyerarşiye ait olup olmadığını kontrol etme. ör .: Araba nesnesi Vehical sınıfına aittir. Dolayısıyla "Vehical'in yeni Car () örneği true değerini döndürür. Ve "new Car (). GetClass (). Eşittir (Vehical.class)" false değerini döndürür, ancak Car nesnesi Vehical sınıfına aittir, ancak ayrı bir tür olarak kategorize edilir.

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.