Il est nécessaire de comparer deux objets en fonction de la classe qu'ils implémentent? Quand comparer en utilisant getClass()
et quand getClass().getName()
? Y a-t-il une différence entre ces approches pour comparer deux types de classe d'objets (noms)?
public abstract class Monster { ... }
public class MonsterTypeOne extends Monster { ... }
public class MonsterTypeTwo extends Monster { ... }
Monster monster = MonsterTypeOne();
Monster nextMonster = MonsterTypeTwo();
if(nextMonster.getClass().getName().equals(monster.getClass().getName()) )// #1
if(nextMonster.getClass().equals(monster.getClass()) )// #2
EDIT 1
Qu'en est-il de: ?
nextMonster.getClass().equals(MonsterTypeOne.class)
Y a-t-il une différence entre ces approches pour comparer deux types de classe d'objets (noms)?
Oui. Deux classes peuvent avoir le même nom si elles sont chargées par différents ClassLoader
s.
Au plus simple, un chargeur de classe crée un espace de nom plat de corps de classe référencés par un nom de chaîne.
"Eclipse - un conte de deux machines virtuelles (et de nombreux chargeurs de classe)" dit
Cela signifie qu'il est possible d'avoir deux classes portant le même nom dans un VM à la fois, à condition qu'elles aient deux ClassLoaders distincts
Quand comparer en utilisant
getClass()
et quandgetClass().getName()
?
Si vous voulez savoir si deux objets sont du même type, vous devez utiliser la méthode equals
pour comparer les deux classes - la première option.
Je ne peux pas imaginer pourquoi vous voudriez faire cela, mais si vous voulez savoir si deux objets avec différents types concrets ont des types avec le même nom complet, alors vous pouvez utiliser le second. Si vous ne comprenez pas les "types concrets" et les "noms pleinement qualifiés" dans le contexte de Java alors vous n'écrivez pas de code d'analyse de type pour Java donc tu ne veux pas.
Utilisez class.equals()
:
if (nextMonster.getClass().equals(monster.getClass()))
ou, parce que chaque classe est comme un singleton - il n'y a qu'une seule instance de chaque classe par chargeur de classe, et la plupart des machines virtuelles Java n'ont qu'un chargeur de classe - vous pouvez même utiliser une comparaison d'identité:
if (nextMonster.getClass() == monster.getClass())
J'ai rencontré un problème en comparant deux classes en utilisant .equals. La solution fournie ci-dessus n'est pas entièrement exacte. La classe n'implémente pas Comparable.
Les références de classe ne sont pas nécessairement de vrais singletons dans une machine virtuelle Java, car vous pouvez avoir plusieurs ClassLoaders.
J'écrivais un plugin Maven qui creusait des annotations dans les beans après la compilation. Le plugin avait un chargeur de classe et j'avais mon propre chargeur de classe. Lors de la comparaison de deux classes du même nom provenant de chargeurs différents, la comparaison échouait.
L'implémentation d'Object.equals ressemble à ceci:
public boolean More ...equals(Object obj) {
return (this == obj);
}
Vous allez donc comparer des références.
Si vous comparez des classes et que vous savez avec certitude qu'il n'y aura qu'un seul chargeur de classe impliqué, vous pouvez utiliser en toute sécurité .equals ou c1 == c2 mais si vous n'êtes pas sûr, vous devez comparer par nom:
if(c1.getName().equals(c2.getName()) {
...
}
Une autre façon d'y parvenir sera de remplacer le hashcode dans la sous-classe.
public interface Parent {
}
public static class Child1 implements Parent{
private String anyfield="child1";
@Override
public int hashCode() {
//Code based on "anyfield"
}
@Override
public boolean equals(Object obj) {
//equals based on "anyfield"
}
}
public static class Child2 implements Parent{
private String anyfield="child2";
@Override
public int hashCode() {
//Code based on "anyfield"
}
@Override
public boolean equals(Object obj) {
//equals based on "anyfield"
}
}
Maintenant, les égaux reviendront si les implémentations/sous-classes sont du même type concret.
public static void main(String args[]){
Parent p1=new Child1();
Parent p2=new Child1();
Parent p3=new Child2();
System.out.println("p1 and p2 are same : "+p1.equals(p2));
System.out.println("p2 and p3 are same : "+p2.equals(p3));
}
Production-
p1 and p2 are same : true
p2 and p3 are same : false
En fait, comparer une classe de confiance par son nom est une faiblesse. Voir https://cwe.mitre.org/data/definitions/486.html
if (nextMonster.getClass() == monster.getClass())
est la bonne façon de comparer les classes
if (nextMonster.getClass().equals(monster.getClass()))
devrait toujours fonctionner pour la même raison que @Bohemian mentionnée