Quel est le motif de préférer getClass () sur instanceof lors de la génération equals ()?

StackOverflow https://stackoverflow.com/questions/596462

Question

J'utilise Eclipse pour générer .equals() et .hashCode(), et il y a une option « Utiliser « instanceof » pour comparer les types ». La valeur par défaut est pour que cette option soit désactivée et utiliser .getClass() pour comparer les types. Y at-il raison que je préférerais .getClass() sur instanceof?

Sans utiliser instanceof:

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

Utilisation instanceof:

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

Je vérifie habituellement l'option instanceof, puis aller et supprimer le cocher « if (obj == null) ». (Il est superflu car les objets nuls toujours l'échec instanceof.) Y at-il raison qui est une mauvaise idée?

Était-ce utile?

La solution

Si vous utilisez instanceof, ce qui rend votre implémentation equals final conservera le contrat de symétrie de la méthode: x.equals(y) == y.equals(x). Si final semble restrictive, d'examiner attentivement votre notion d'équivalence d'objets pour vous assurer que vos mises en œuvre prépondérants maintenir intégralement le contrat établi par la classe Object.

Autres conseils

Josh Bloch favorise votre approche:

  

La raison pour laquelle je suis favorable à l'approche instanceof est que lorsque vous utilisez l'approche getClass, vous avez la restriction que les objets ne sont égaux à d'autres objets de la même classe, le même type de temps d'exécution. Si vous étendez une classe et d'ajouter quelques méthodes anodines à, puis vérifier si un objet de la sous-classe est égal à un objet de la classe super, même si les objets sont égaux dans tous les aspects importants, vous obtiendrez le surprenante réponse qu'ils ne sont pas égaux. En fait, cela viole une interprétation stricte du principe de substitution Liskov , et peut conduire à un comportement très surprenant. En Java, il est particulièrement important parce que la plupart des collections (HashTable, etc.) sont basées sur la méthode equals. Si vous mettez un membre de la super classe dans une table de hachage comme la clé, puis le regarder à l'aide d'une instance de la sous-classe, vous ne trouverez pas, parce qu'ils ne sont pas égaux.

Voir aussi cette réponse SO .

couvre également cela.

Secrets d'égaux obtient dans cette discussion avec une longue et détaillée pour quelques exemples courants et bien connus, y compris par Josh Bloch et Barbara Liskov, découvrir quelques problèmes dans la plupart d'entre eux. Elle obtient également dans le instanceof vs getClass. Certains citent des extraits

  

Conclusions

     

Après avoir disséqué les quatre exemples choisis arbitrairement de mise en oeuvre d'égal à égal (), que devons-nous en conclure?

     

D'abord: il y a deux façons très différentes d'effectuer la vérification de la correspondance de type dans une mise en œuvre d'égal à égal (). Une classe peut permettre une comparaison de type mixte entre les objets super- et sous-classe au moyen de l'opérateur instanceof, ou une classe peut traiter des objets de type différent que la non-égale au moyen du test getClass (). Les exemples ci-dessus illustrent bien que les mises en œuvre d'égal à égal () en utilisant getClass () sont généralement plus robustes que celles mises en oeuvre en utilisant instanceof.

     

Le test instanceof est correct que pour les classes finales ou si au moins égal à la méthode () est définitive dans une superclasse. Ce dernier implique essentiellement qu'aucune sous-classe doit étendre l'état de superclasse, mais ne peut ajouter des fonctionnalités ou des champs qui ne sont pas pertinents pour l'état et le comportement de l'objet, tels que les champs de transitoires ou statiques.

     

en utilisant le contrat Implémentations essai getClass () d'autre part toujours respecter les égaux (); ils sont corrects et robustes. Ils sont, cependant, sémantiquement très différent des mises en œuvre qui utilisent le test instanceof. À l'aide getClass Implémentations () ne permettent de comparer avec des objets sous-superclasse, pas même lorsque la sous-classe n'ajoute les champs et ne veulent même pas surcharger les méthodes equals (). Une telle extension de classe « trivial » serait par exemple l'ajout d'une méthode debug-print dans une sous-classe définie dans ce but précis « trivial ». Si la superclasse interdit la comparaison de type mixte via le contrôle getClass (), l'extension triviale ne serait pas comparable à sa superclasse. Que ce soit un problème ou non dépend entièrement de la sémantique de la classe et le but de l'extension.

La raison d'utiliser getClass est d'assurer la propriété symétrique du contrat de equals. De les JavaDocs de ses pairs:

  

Il est symétrique: pour toute non nulle   les valeurs de référence x et y, x.equals (y)   devrait retourner vrai si et seulement si   y.equals (x) renvoie vrai.

En utilisant instanceof, il est possible de ne pas être symétrique. Prenons l'exemple: étend chien animal. Le equals de l'animal fait un contrôle des animaux de instanceof. Le equals de chien fait un chèque instanceof de chien. Donnez des animaux et chien d (avec d'autres champs de la même):

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

Ceci viole la propriété symétrique.

Pour suivre strictement contrat égale, la symétrie doit être assurée, et donc la classe doit être le même.

Ceci est quelque chose d'un débat religieux. Les deux approches ont leurs problèmes.

  • Utilisez instanceof et vous ne pouvez jamais ajouter des membres importants aux sous-classes.
  • Utilisez getClass et vous violer le principe de substitution Liskov.

Bloch a une autre pièce pertinente de conseils en effective Java deuxième édition :

  • Point 17: Conception et documents pour l'héritage ou l'interdire

moi si je me trompe, mais getClass () sera utile lorsque vous voulez vous assurer que votre instance n'est pas une sous-classe de la classe que vous comparez avec. Si vous utilisez instanceof dans cette situation, vous ne pouvez pas savoir que, parce que:

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

Si vous voulez vous assurer que cette classe correspondra à utiliser alors getClass() ==. Si vous voulez faire correspondre les sous-classes puis instanceof est nécessaire.

En outre, instanceof ne correspond pas à l'encontre d'un nul, mais est sûr de comparer à une valeur nulle. Donc, vous ne devez pas null vérifier.

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

Cela dépend si vous considérez si une sous-classe d'une classe donnée est à son parent est égal.

class LastName
{
(...)
}


class FamilyName
extends LastName
{
(..)
}

ici j'utiliser « instanceof », parce que je veux un LastName à comparer à FamilyName

class Organism
{
}

class Gorilla extends Organism
{
}

ici j'utiliser « getClass », parce que la classe dit déjà que les deux cas ne sont pas équivalents.

instanceof fonctionne pour instences de la même classe ou ses sous-classes

  

Vous pouvez l'utiliser pour tester si un objet est une instance d'une classe, une instance d'une sous-classe ou une instance d'une classe qui implémente une interface particulière.

ArryaList et RoleList sont à la fois instanceof Liste

Alors que

getClass () == o.getClass () sera vrai que si les deux objets (ce et o) appartient à la même classe.

Donc, en fonction de ce que vous devez vous comparer pouvez utiliser l'un ou l'autre.

Si votre logique est: « Un des objets est égale à d'autres que si elles sont à la fois la même classe » vous devriez aller pour le « égaux », qui je pense est la plupart des cas.

Les deux méthodes ont leurs problèmes.

Si la sous-classe change l'identité, alors vous avez besoin de comparer leurs classes réelles. Dans le cas contraire, vous violez la propriété symétrique. Par exemple, les différents types de Persons ne doivent pas être considérés comme équivalents, même si elles ont le même nom.

Cependant, certaines sous-classes ne changent pas l'identité et ceux-ci doivent utiliser instanceof. Par exemple, si nous avons un tas d'objets immuables Shape, puis une Rectangle avec la longueur et la largeur de 1 doit être égale à l'unité Square.

Dans la pratique, je pense que le premier cas est plus susceptible d'être vrai. Habituellement, sous-classement est un élément fondamental de votre identité et d'être exactement comme vos parents, sauf que vous pouvez faire une petite chose ne vous rend pas égal.

En fait vérifier instanceof où un objet appartient à une hiérarchie ou non. ex: objet voiture appartient à vehical classe. Donc, « nouvelle instance de voitures () de Vehical » renvoie vrai. Et "nouveaux égaux (Vehical.class). Car (). GetClass ()" return false, si l'objet de voiture appartient à vehical classe, mais il est classé comme un type distinct.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top