it-roy-ru.com

Как реализовать метод "равных" для обобщений, используя "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, как вы можете ясно видеть, поэтому, пожалуйста, объясните немного подробнее!

19
David T.

Эта версия не дает никаких предупреждений

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

21
Evgeniy Dorofeev

Решение Евгения и рассуждения Михала верны - вам не нужно беспокоиться о типе T здесь. Причина в том, что метод equals не зависит от универсальности для правильной работы. Вместо этого он объявляется Object и принимает Object. Таким образом, он отвечает за проверку типа времени выполнения того, что было передано.

Если this оказывается SingularNode<String> и вы сравниваете его с SingularNode<Integer>, то ((SingularNode<?>)other).value.equals(value) прекрасно, потому что вызов Integer.equals с аргументом String вернет false.

5
Paul Bellora

Я положил ответ здесь, чтобы поставить код ..

В вашем примере у вас есть (в псевдокоде) 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;
}

Идя по этому пути, вам не нужно беспокоиться о кастинге.

1
Michal Borek

У меня та же проблема, но она более общая. У меня есть класс, где у меня есть 3 общих типа. Мне не нужно хранить какие-либо переменные этих типов, потому что этот класс используется для преобразования. Однако, есть сохраненные переменные 'request', и я использую кеш на основе этого класса, поэтому мне нужно реализовать метод 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, внутренние системы ...) Нам нужна функция отображения из одного типа в другой. Мы используем кеширование для повышения производительности, и единственным человеком, занимающим промежуточное положение, является этот класс.

0
JiangHongTiao