Overriding the equals method without breaking symmetry in a class that has a primary key

the answer to this question is probably "impossible", but let me ask independently :)

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 the collection, that is, I have to override the equals method. Not quite a trivial matter, but afloat I will have something like:

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

Sorry for any obvious mistakes by simply typing this out of my head. Now, let's say later, in the application I have ssid, which I received through user input. If I want to compare my ssid with Person, I will need to call something like:

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

This means that I have to create a convenience constructor to create a Person based on ssid (if I don't already have one), and that is also pretty verbose. It would be much easier to just call

 myPerson.equals(mySsid) 

but if I added string comparisons to the Person equals class, this would break the symmetry property, since String has no idea how to compare itself to Person.

So, finally, the big question is, is there a way to enable such โ€œ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 requiring an exact solution. The equations should take into account the cases when I want to extract the Person from the collection. Therefore, it should be possible to do something like this:

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

It would seem obvious and intuitive to be able to define equality in a class based on a primary key, but I did not find a direct way to do this.

+4
source share
6 answers

Itโ€™s best to store your people on a map, then you can easily download them:

 HashMap<String, Person> people = new HashMap<String, Person>(); Person p = constructPersonFromStuff(); people.put(p.ssid, p); 

and then you will see if the person exists:

 String ssid = getFromSomewhere(); if(people.contains(ssid)){ Person thatGuy = people.get(ssid); }else{ //that person DOESN'T EXIST! HE A FIGMENT OF YOUR IMGAINATION! } 
+3
source

You are not comparing two Person s, you are comparing two ssid s. I would use:

 myPerson.getSsid().equals(mySsid); 
+2
source

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

I just do not understand your problem. You have this code:

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

It just doesn't look bad to me. I think you could define a function for this:

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

But it really is not such a big improvement.

What is the problem?

+1
source

I would say that Person is not equal to ssid. The fact that you are using the ssid comparison to determine if two people are equal is a shortcut - we accept as an agreement that two people with the same ssid refer to the same real person - but this does not really make equality true.

Perhaps you really want here to give your Lin the hasSsid () boolean method. Or just call myPerson.getSsid().equals(mySsid) .

+1
source

The equals method should never return true unless its argument is an instance of the class in which the method was called.

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

 public interface Identifiable { public Serializable getSsid(); } public class Person implements Identifiable { ... 

you can write your code in general terms in terms of Identifiable ... this can help.

(I assume that the real problem is to process your identifiable objects in a general way for utilities, etc.).

0
source

You can 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 the factory method to create EntityStore instances so that you can switch to optimized / fantastic implementations if necessary.

0
source

Source: https://habr.com/ru/post/1308791/


All Articles