Переопределение объекта # равно (объект) вне класса
Можно ли переопределить Object#equals(Object)
локально при использовании list.contains(someObject)
?
Пример:
class SomeObject {
...
private int id;
private String name;
@Overrdide
public boolean equals(Object other){
...
return this.id == other.id;
}
}
Но что, если я хочу другого типа равных, когда я использую list.contains(someObject)
? Например, я хочу знать, содержит ли список определенное имя? Возможно ли переопределить Object#equals(Object)
"анонимно"?
Более конкретное объяснение, почему мне это нужно:
int objectId = ... // Some event which passes me the attribute of an object but not the object itself
Теперь у меня есть List<SomeObject> someObjects
, и я хотел бы знать, содержит ли этот список объект с objectId
, не обязательно повторяя его.
Одно "решение", о котором я мог подумать, будет использовать Map<Integer, SomeObject> mapping
, а затем someObject = mapping.get(objectId)
EDIT: Мой вопрос не является дубликатом, так как я специально прошу переопределить Object#equals(Object)
.
Ответы
Ответ 1
Вы можете применить фильтр к объекту потока, полученному из списка. Фильтр принимает предикат, где вы можете установить условие. Затем вы можете проверить, не отфильтрован ли поток потока:
list.stream().filter(e -> e.name.equals(otherName)).findAny().isPresent()
Вы можете сделать его повторно используемым, например, следующим образом:
private <T> boolean containsWithPredicate(List<T> list, Predicate<T> predicate) {
return list.stream().filter(predicate).findAny().isPresent();
}
и назовите его следующим образом:
boolean containsElement = containsWithPredicate(myListOfObjects,
(e -> e.name.equals(otherName)));
EDIT:
Существует гораздо более простой способ сделать то же самое выше, просто позвонив Stream.anyMatch
вместо выполнения filter
, затем findAny
list.stream().anyMatch(predicate);
Ответ 2
Вы можете использовать TreeSet
с пользовательским Comparator
для достижения желаемого. Он не использует equals()
для равенства, а скорее считает, что если compare()
возвращает 0
, объекты равны.
Скажем, мы хотим, чтобы все Strings
были равны, если они имеют одинаковую длину:
TreeSet<String> set = new TreeSet(new Comparator<>() {
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
}
set.add("asd");
set.contains("foo"); // Returns true
Это может быть полезной конструкцией (работает также с TreeMap
) в тех случаях, когда вам нужно иметь разные определения равенства для объектов в разное время, все еще работая с коллекциями.
Ответ 3
Нет способа иметь несколько equals
для одного и того же класса. Но вы можете подклассифицировать свой класс и переопределить hashCode()
и equals()
, а затем использовать нужный экземпляр в зависимости от equals
, который вы хотите использовать.
Я должен сказать, что мне не нравится это делать, метод equals
должен быть хорошо определен, будет очень сложно использовать несколько equals
для разных сценариев. Вместо подкласса, у меня было бы полное разное выполнение, если мне нужно иметь два разных метода equals
. Поэтому я настоятельно рекомендую вам пересмотреть свой дизайн.