Переопределение метода equals без нарушения симметрии в классе с первичным ключом - PullRequest
4 голосов
/ 04 мая 2010

ответ на этот вопрос, вероятно, "невозможен", но позвольте мне спросить независимо от этого:)

Предполагается, что у нас есть очень простой класс JAVA с первичным ключом, например:

class Person {
    String ssid;
    String name;
    String address;
    ...
}

Теперь я хочу сохранить людей в коллекции, то есть мне придется переопределить метод equals. Не совсем тривиальное дело, но на голой основе у меня будет что-то вроде:

@Override
public boolean equals (Object other) {
    if(other==this) return true;
    if(!other.getClass().equals(this.getClass()) return false;
    Person otherPerson = (Person)other;
    if(this.ssid.equals(otherPerson.getSsid()) return true;
}

Извините за любые очевидные ошибки, просто напечатайте это из моей головы. Теперь, скажем позже, в приложении у меня есть ssid, который я получил через пользовательский ввод. Если я хочу сравнить свой ssid с человеком, мне нужно будет позвонить примерно так:

String mySsid = getFromSomewhere();
Person myPerson = getFromSomewhere();
if(myPerson.equals(new Person(mySsid)) doSomething();

Это означает, что мне нужно создать удобный конструктор для создания Person на основе ssid (если у меня его еще нет), и это также довольно многословно. Было бы гораздо приятнее просто позвонить

myPerson.equals(mySsid)

но если бы я добавил сравнение строк в свой класс Person equals, это нарушило бы свойство симметрии, поскольку строка не имела понятия о том, как сравнивать себя с Person.

Итак, наконец, главный вопрос, есть ли способ разрешить такого рода «стенографические» сравнения, используя метод переопределенных равных, без нарушения правила симметрии?

Спасибо за любые мысли по этому поводу!

РЕДАКТИРОВАТЬ: Просто чтобы уточнить, это больше открытый вопрос, чем проблема, которая ищет точное решение. Равные должны обслуживать случаи, когда я хочу извлечь человека из коллекции. Так что должно быть возможно сделать что-то вроде этого:

List<Person> people = ...
people.get(ssid);

Казалось бы, очевидно и интуитивно понятно определить равенство в классе на основе первичного ключа, но я не нашел простого способа сделать это.

Ответы [ 6 ]

3 голосов
/ 04 мая 2010

Лучше всего хранить своих людей на карте, тогда их можно легко найти:

HashMap<String, Person> people = new HashMap<String, Person>();

Person p = constructPersonFromStuff();

people.put(p.ssid, p);

, а затем вы можете увидеть, существует ли человек:

String ssid = getFromSomewhere();

if(people.contains(ssid)){
  Person thatGuy = people.get(ssid);
}else{
  //that person DOESN'T EXIST! HE'S A FIGMENT OF YOUR IMGAINATION!
}
2 голосов
/ 04 мая 2010

Вы сравниваете не два Person с, вы сравниваете два ssid с.Я бы использовал:

myPerson.getSsid().equals(mySsid);
1 голос
/ 04 мая 2010

Я бы сказал, что Персона не равна ссиде. Тот факт, что вы используете сравнение ssid, чтобы определить, равны ли два человека, является сокращением - мы согласны с тем, что два человека с одним и тем же ssid ссылаются на одного и того же человека в реальном мире - но на самом деле это не то, что делает равенство правда.

Возможно, вы действительно хотите, чтобы ваш Person использовал логический метод hasSsid (). Или просто позвоните myPerson.getSsid().equals(mySsid).

1 голос
/ 04 мая 2010

Нет смысла говорить, что Person равен строке, не идите по этому пути.

Я просто не понимаю твою проблему. У вас есть этот код:

String mySsid = getFromSomewhere();
Person myPerson = getFromSomewhere();
if (myPerson.getSsid().equals(mySsid) doSomething();

Это не выглядит плохо для меня. Я думаю, вы могли бы определить функцию, чтобы сделать это для вас:

if (myPerson.ssidEquals(mySsid)) doSomething();

Но это действительно не так уж и много.

В чем проблема?

0 голосов
/ 04 мая 2010

Вы можете создать пользовательскую структуру данных, которая использует карту или установлена ​​внутри:

public interface EntityStore<T,K> {
    T get(K id);
    boolean contains(Object o);
    void put(K id, T entity);
    void remove(Object o);
    // ...
}

public class MapEntityStore<T,K> {
    private Map<K,T> entities = new HashMap<K,T>();
    public T get(K id) { return entities.get(id); }
    public boolean contains(Object o) {
        if (entities.keySet().contains(o))
            return true; // search by id
        if(entities.values().contains(o))
            return true; // search by value (this can be optimized if necessary)

        return false;
    }
    ...
}

Вы можете использовать фабричный метод для создания экземпляров EntityStore, чтобы при необходимости переходить на оптимизированные / причудливые реализации.

0 голосов
/ 04 мая 2010

метод equals никогда не должен возвращать true, если его аргумент не является экземпляром класса, для которого был вызван метод.

если хотите, вы можете создать интерфейс, скажем, Identifiable.

public interface Identifiable {
    public Serializable getSsid();
}

public class Person implements Identifiable { ...

затем вы можете написать свой код в общем виде с точки зрения опознаваемого ... это может помочь.

(я предполагаю, что реальная проблема заключается в обработке идентифицируемых вами объектов в общем виде для служебных подпрограмм и тому подобного)

...