Сравнение двух классов по типам или именам классов
Нужно ли сравнивать два объекта на основе класса, который они реализуют? Когда сравнивать getClass()
и getClass().getName()
?
Есть ли разница между этими подходами для сравнения двух типов (имен) классов объектов?
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
ИЗМЕНИТЬ 1
Как насчет:?
nextMonster.getClass().equals(MonsterTypeOne.class)
Ответы
Ответ 1
Есть ли разница между этими подходами для сравнения двух типов (имен) классов объектов?
Да. Два класса могут иметь одно и то же имя, если они загружаются разными ClassLoader
s.
"Основы загрузчиков классов Java" сообщает
В своем простейшем случае загрузчик классов создает плоское пространство имен тел класса, на которые ссылается имя строки.
"Eclipse - рассказ о двух виртуальных машинах (и многих загрузчиках классов)" сообщает
Это означает, что возможно иметь два класса с одним и тем же именем, загруженные в виртуальную машину одновременно, при условии, что у них есть два отдельных класса ClassLoaders
Когда сравнивать getClass()
и getClass().getName()
?
Если вы хотите знать, являются ли два объекта одного типа, вы должны использовать метод equals
для сравнения двух классов - первого варианта.
Я не могу представить, почему вы хотите это сделать, но если вы хотите знать, имеют ли два объекта с разными конкретными типами с одним и тем же полным именем, вы можете использовать второй. Если вы не понимаете "конкретные типы" и "полностью квалифицированные имена" в контексте Java, вы не пишете код анализа типа для Java, поэтому не хотите.
Ответ 2
Используйте class.equals()
:
if (nextMonster.getClass().equals(monster.getClass()))
или, потому что каждый класс похож на singleton - есть только один экземпляр каждого класса для JVM - вы даже можете использовать сравнение идентичности:
if (nextMonster.getClass() == monster.getClass())
Ответ 3
Я столкнулся с проблемой сравнения двух классов с использованием .equals. Вышеприведенное решение не совсем точно. Класс не реализует Comparable.
Ссылки на классы не обязательно являются истинными синглетонами в JVM, потому что вы можете иметь несколько ClassLoaders.
Я писал плагин Maven, который выкапывал аннотации из beans после компиляции. У плагина был один загрузчик классов, и у меня был свой собственный загрузчик классов. При сравнении двух классов одного и того же имени с разными загрузчиками сравнение потерпит неудачу.
Реализация Object.equals выглядит так:
public boolean More ...equals(Object obj) {
return (this == obj);
}
Итак, вы будете сравнивать ссылки.
Если вы сравниваете классы и знаете, что будет только один загрузчик классов, вы можете безопасно использовать .equals или c1 == c2, но если вы не уверены, что вам следует сравнивать по имени:
if(c1.getName().equals(c2.getName()) {
...
}
Ответ 4
На самом деле сравнение доверенного класса по имени - это слабость.
См. https://cwe.mitre.org/data/definitions/486.html
if (nextMonster.getClass() == monster.getClass())
- правильный способ сравнения классов
if (nextMonster.getClass().equals(monster.getClass()))
должен по-прежнему работать по той же причине @Bohemian, упомянутый
Ответ 5
Другим способом достижения этого будет переопределение хэш-кода в подкласс.
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"
}
}
Теперь равны будут возвращаться, если реализации/подклассы имеют один и тот же конкретный тип.
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));
}
Вывод -
p1 and p2 are same : true
p2 and p3 are same : false