views:

93

answers:

6

Hi,

the answer to this question is probably "not possible", but let me ask regardless :)

Assuming we have a very simple JAVA class that has a primary key, for example:

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

Now, I want to store people in a collection, meaning I will have to override the equals method. Not a completely trivial matter, but on a bare basis I will have something along the lines of:

@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;
}

Excuse any obvious blunders, just typing this out of my head. Now, let's say later on in the application I have a ssid I obtained through user input. If I want to compare my ssid to a Person, I would have to call something like:

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

This means I have to create a convenience constructor to create a Person based on ssid (if I don't already have one), and it's also quite verbose. It would be much nicer to simply call

myPerson.equals(mySsid)

but if I added a string comparison to my Person equals class, that would break the symmetry property, since the String hasn't got a clue on how to compare itself to a Person.

So finally, the big question, is there any way to enable this sort of "shorthand" comparisons using the overriden equals method, and without breaking the symmetry rule?

Thanks for any thoughts on this!

EDIT: Just to clarify, this is more of an open question than a problem seeking an exact solution. The equals should cater for cases when I want to extract a Person from a collection. So it should be possible to do something like this:

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

It would appear obvious and intuitive to be able to define equality on a class based on a primary key, but I haven't found a straightforward way of doing that.

+2  A: 

You're not comparing two Persons, you're comparing two ssids. I would use:

myPerson.getSsid().equals(mySsid);
Bill the Lizard
True, I suppose I should have clarified that this should apply to fetching People out of collections too. So for example people.get(ssid) should ideally work as well.
Kosta
Why can't you use the SSID as the key in a map? Then you don't have to worry about breaking all those symmetry rules.
Kylar
@Kosta: Ah, now that makes things more interesting. I'll think about this a bit more.
Bill the Lizard
@Kosta: Wait, what am I thinking? If you have a Collection with a `get(Object o)` method, that's a map, right?
Bill the Lizard
+3  A: 

A better bet is to store your People in a map, then you can retrieve them easily:

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

Person p = constructPersonFromStuff();

people.put(p.ssid, p);

and then later, you can see if the person exists:

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!
}
Kylar
I realize this doesn't answer your original question, but it seems like this is what you're trying for anyway :)
Kylar
+1  A: 

It makes no sense to say that a Person is equal to a string, do not go down that path.

I just don't understand your problem. You have this code:

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

That just doesn't look bad to me. I guess you could define a function to do that for you:

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

But that's really not that big improvement.

What's the problem?

Frank Krueger
+1  A: 

I'd argue that a Person isn't equal to an ssid. The fact that you use ssid comparison to determine whether two Persons are equal is a shortcut -- we accept as a convention that two Persons with the same ssid refer to the same real-world person -- but isn't actually what makes the equality true.

Perhaps what you really want here is to give your Person a boolean "hasSsid()" method. Or just call myPerson.getSsid().equals(mySsid).

JacobM
A: 

the equals method must never return true when its argument is not an instance of the class on which the method was invoked.

if you like, you can create an interface, say Identifiable.

public interface Identifiable {
    public Serializable getSsid();
}

public class Person implements Identifiable { ...

you can then write your code in a generic way in terms of Identifiable ... this might help.

(i'm assuming that the real problem is to handle your identifiable objects in a generic fashion for utility routines and such).

LES2
A: 

You could create a custom data structure that uses a map or set internally:

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;
    }
    ...
}

You can use a factory method to create instances of EntityStore so that you can change to optimized / fancy implementations as necessary.

LES2