Как реализовать метод "equals" для генериков с использованием "instanceof"?
У меня есть класс, который принимает общий тип, и я хочу переопределить метод equals
не-неудобным способом (т.е. то, что выглядит чисто и имеет минимальный объем кода, но для очень общего использования).
Сейчас у меня есть что-то вроде этого:
public class SingularNode<T> {
private T value;
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object other){
if(other instanceof SingularNode<?>){
if(((SingularNode<T>)other).value.equals(value)){
return true;
}
}
return false;
}
}
Что, я предполагаю, довольно ошибочен - я делаю приведение к SingularNode<T>
объекта other
, что потенциально может вызвать ошибку.
Другое дело - когда я делаю if(other instanceof SingularNode<?>)
, я на самом деле не проверяю правильную вещь. Я действительно хочу проверить тип T
, а не тип ?
. Всякий раз, когда я пытаюсь сделать ?
в T
, я получаю некоторую ошибку, например:
Невозможно выполнить проверку экземпляра с параметризованным типом SingularNode<T>
. Вместо этого используйте форму SingularNode<?>
, так как дополнительная информация о типовом типе будет стерта во время выполнения
Как я могу обойти это? Есть ли способ сделать T.class.isInstance(other);
?
Я полагаю, есть одно действительно уродливое решение для взлома:
@SuppressWarnings("unchecked")
public boolean isEqualTo(Class<?> c, Object obj){
if(c.isInstance(obj) && c.isInstance(this)){
if(((SingularNode<T>)obj).value.equals(value)){
return true;
}
}
return false;
}
Но это просто выглядит неудобно с дополнительным параметром метода, а также не встроенная функция типа equals
.
Любой, кто разбирается в дженериках, объясните это? Я не настолько разбираюсь в Java, как вы можете ясно видеть, поэтому, пожалуйста, объясните немного подробнее!
Ответы
Ответ 1
Эта версия не дает предупреждений
public boolean equals(Object other){
if (other instanceof SingularNode<?>){
if ( ((SingularNode<?>)other).value.equals(value) ){
return true;
}
}
return false;
}
Что касается кастинга на SingularNode<T>
, это ничего не поможет, вы не можете предположить, что T
может быть чем угодно, кроме Object
.
Подробнее о том, как сгенерированы обобщенные файлы на Java в
https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
Ответ 2
Решение Evgeniy и аргументы Михала являются правильными - вам не нужно беспокоиться о типе T
здесь. Причина в том, что метод equals
не зависит от правильной работы дженериков. Вместо этого он объявляется Object
, и он принимает Object
. Таким образом, он отвечает за проверку типа выполнения того, что было передано.
Если this
оказывается SingularNode<String>
, и вы сравниваете его с SingularNode<Integer>
, тогда ((SingularNode<?>)other).value.equals(value)
отлично, потому что вызов Integer.equals
с аргументом String
будет правильно возвращать false
.
Ответ 3
Я поставил здесь ответ, чтобы поставить код.
В вашем примере у вас есть (в псевдокоде) Integer(5).equals(Char('k'))
, который равен false
, в соответствии со следующей версией equals на java.lang.Integer
:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
Идя таким образом, вам не нужно беспокоиться о кастинге.
Ответ 4
У меня та же проблема, однако она более общая. У меня есть класс, где у меня есть 3 типа. Мне не нужно сохранять какую-либо переменную этих типов, потому что этот класс используется для преобразования. Однако здесь хранятся переменные "запрос", и я использую кеш на основе этого класса, поэтому мне нужно реализовать метод equals(), основанный на этих генериках.
Знаете ли вы, есть ли какой-нибудь подход, как сделать это без отражения? Может быть, внутренняя переменная этого типа.. Однако тогда она равна нулю.
public class TheClass<I, O, M> {
private ClassA param1;
private ClassB param2;
private ClassC<M> param3;
private BiFunction<ClassC<M>, I, Optional<O>> mapping;
public ClassD<O, M> doSomething(ClassD<I, M> param) {
...
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
if (getClass() != o.getClass()) {
return false;
}
TheClass<?, ?, ?> that = (TheClass<?, ?, ?>) o;
return Objects.equals(getParam1(), that.getParam1()) &&
Objects.equals(getParam2(), that.getParam2()) &&
Objects.equals(getParam3(), that.getParam3());
}
}
Для лучшего воображения... У меня есть набор объектов DAO, получающих данные из базы данных. С другой стороны, у нас есть еще один набор поставщиков API, которые предоставляют похожие данные в другом формате (REST, внутренние системы..)
Нам нужна функция отображения от одного типа к другому. Мы используем кеширование для лучшей производительности, и единственным человеком в середине является этот класс.
Ответ 5
Вам не нужно использовать кастинг.
Лучше всего соответствует реализации. Я вижу это как
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof VehicleModel)) return false;
VehicleModel that = (VehicleModel) o;
if (vehicleName != null ? !vehicleName.equals(that.vehicleName) : that.vehicleName != null)
return false;
return true;
}