Переопределение метода "равно": как определить тип параметра?
Я пытаюсь переопределить метод equals
для параметризованного класса.
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Tuple))
return false;
Tuple<E> other = (Tuple<E>) obj; //unchecked cast
if (!a0.equals(other.a0) && !a0.equals(other.a1)) {
return false;
}
if (!a1.equals(other.a1) && !a1.equals(other.a0)) {
return false;
}
return true;
}
Как я могу убедиться, что <E>
объекта other
совпадает с this
?
Ответы
Ответ 1
Вы можете сделать это, сохранив ссылку на тип Class<E>
. Однако, на мой взгляд, тесты на равенство должны относиться к значениям, которые представляют объекты, а не к конкретным типам, которые выражают значения.
Классическим примером этого является API коллекций, например. new ArrayList<String>().equals(new LinkedList<Object>())
возвращает true
. Хотя они имеют совершенно разные типы, они представляют одно и то же значение, а именно "пустую коллекцию".
Лично, если два Tuple
, которые представляют одни и те же данные (например, ("a", "b")
), не равны, поскольку один имеет тип Tuple<String>
, а другой - Tuple<Object>
?
Ответ 2
Из-за стирания вы не можете. О том, что вы можете сделать, это сохранить в классе кортежа тип, который вы планируете удерживать Tuple в элементе поля "java.lang.Class". Затем вы можете сравнить эти поля, чтобы убедиться, что класс кортежа поддерживает одни и те же типы.
Также смотрите эту тему:
Что эквивалентно С++ Pair < L, R > в Java?
Это поможет, если вы опубликуете больше о своем классе. Я думаю, что неконтролируемый актерский состав и ваше количество полей, которые вы приравниваете, означает, что он должен быть Tuple < E, F > нет?
EDIT: вот полезный класс Pair, который я использую регулярно (при необходимости вы можете адаптировать свой класс Tuple). Обратите внимание, что аналогичные предложения других людей этот класс просто позволяет содержащимся членам решать вопрос о равенстве. Ваш вариант использования - это то, что должно определить, действительно ли равенство основано на типе содержащихся элементов.
/**
* Adapted from http://forums.sun.com/thread.jspa?threadID=5132045
*
*
* @author Tim Harsch
*
* @param <L>
* @param <R>
*/
public class Pair<L, R> {
private final L left;
private final R right;
public R getRight() {
return right;
} // end getter
public L getLeft() {
return left;
} // end getter
public Pair(final L left, final R right) {
this.left = left;
this.right = right;
} // end constructor
public static <A, B> Pair<A, B> create(A left, B right) {
return new Pair<A, B>(left, right);
} // end factory method
@Override
public final boolean equals(Object o) {
if (!(o instanceof Pair<?,?>))
return false;
final Pair<?, ?> other = (Pair<?, ?>) o;
return equal(getLeft(), other.getLeft()) && equal(getRight(), other.getRight());
} // end method
public static final boolean equal(Object o1, Object o2) {
if (o1 == null) {
return o2 == null;
}
return o1.equals(o2);
} // end method
@Override
public int hashCode() {
int hLeft = getLeft() == null ? 0 : getLeft().hashCode();
int hRight = getRight() == null ? 0 : getRight().hashCode();
return hLeft + (37 * hRight);
} // end method
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('<');
if( left == null ) {
sb.append("null");
} else {
sb.append(left.toString());
} // end if
sb.append(',');
if( right == null ) {
sb.append("null");
} else {
sb.append(right.toString());
} // end if
sb.append('>');
return sb.toString();
} // end method
} // end class
Ответ 3
Я просто столкнулся с этой проблемой сам, и в моем-частном случае мне не нужно было знать тип E.
Например:
public class Example<E> {
E value;
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Example<?> other = (Example<?>) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
В приведенном выше коде нет непроверенного списка из-за использования Example<?>
. Подстановочный знак параметра типа?? сохраняет день.
Ответ 4
Так как generics удаляются во время компиляции, вы в принципе не можете. Во время выполнения любой параметр типа ушел, и что касается JVM, они абсолютно одинаковы во всех отношениях.
Способ работы с ним состоит в том, чтобы сохранить поле Class
, которое представляет этот тип, и создать объект с этим типом в конструкторе.
Пример конструктора:
public class Tuple < E > {
public Tuple(Class<E> c) {
//store the class
}
}
Или вы можете использовать factory:
public static <E> Tuple <E> getTuple(Class<E> type) {
// Create and return the tuple,
// just store that type variable in it
// for future use in equals.
}
Ответ 5
К сожалению, вы не можете сделать это во время компиляции; информация исчезла. Таковы последствия стирать стили. Альтернативой является сохранение параметра в качестве экземпляра Class
, а затем поиск его позже.
Ответ 6
Рекомендации по сохранению ссылки на тип E
с объектом Class
кажутся неэффективными (что много бессмысленных ссылок на Class
, занимающих память) и бессмысленно для вашей проблемы.
В общем случае не верно, что Foo<Bar>
и Foo<Baz>
должны быть неравными. Поэтому вам не нужно E
. И в коде, который вы написали, вам даже не нужно его компилировать. Просто добавьте к Tuple<?>
, так как это действительно все, что вы знаете о Tuple
в этой точке. Он все еще компилируется и все.
Если вы пройдете Tuple
с данными двух совершенно разных типов, эти элементы не будут equals
, и ваш метод вернет false
, если это необходимо. (Одна надеется - зависит от того, какие типы реализуют equals
здраво.)
Ответ 7
Используйте отражение. http://tutorials.jenkov.com/java-reflection/generics.html
Ответ 8
Вне темы - понимаете ли вы, что согласно вашей реализации Tuple (a0, a1) равен Tuple (a1, a1)? Я подозреваю, что не то, что вы хотите...
По теме, как говорили другие, стирание делает это невозможным. Но вы должны пересмотреть, почему вы этого хотите - проверка равенства происходит только во время выполнения, а generics - это только время компиляции. Понятно, что переменная имеет общие параметры, но объект этого не делает. Таким образом, когда вы сравниваете равенство объектов, общие параметры вообще не имеют значения; вы не можете принимать какие-либо соответствующие действия на их основе во время выполнения.
Идентификация объекта и общий параметр /contra -variance являются двумя ортогональными проблемами.
Ответ 9
Я согласен с вышеприведенными замечаниями, почему класс E должен быть равен? и как вы хотите обрабатывать подклассы E?
В любом случае, учитывая, что здесь приведен пример кода, который может вам помочь:
public class Example<T> {
T t;
public Example(T t) {
this.t = t;
}
public static void main(String[] args) {
final String s = "string";
final Integer i = 1;
final Number n = 1;
final Example<String> exampleString = new Example<String>(s);
final Example<Integer> exampleInteger = new Example<Integer>(i);
final Example<Number> exampleNumber = new Example<Number>(n);
System.out.println("exampleString subclass " + exampleString.t.getClass());
System.out.println("exmapleIntger subclass " + exampleInteger.t.getClass());
System.out.println("exmapleNumber subclass " + exampleNumber.t.getClass());
System.out.println("Integer equals Number = " +
exampleInteger.t.equals(exampleNumber.t));
}
}
Вы можете вызвать t.getClass(), чтобы получить информацию о типе типа T (предполагая, что это не нуль, конечно.)
Надеюсь, это поможет.