Переопределение объекта # равно (объект) вне класса

Можно ли переопределить 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. Поэтому я настоятельно рекомендую вам пересмотреть свой дизайн.