Bad practice - Class defines compareTo(...) and uses Object.equals() - java

Wondering what needs to be done for listed method
public final int compareTo(final FieldDTO o) {
return o.available.compareTo(this.available);
its throwing exception on line 2 stating
Bad practice - Class defines compareTo(...) and uses Object.equals() 16 days
field defines compareTo(FieldDTO) and uses Object.equals()
Not sure how should i handle this.
Thanks in advance.

If you define compareTo you should at least define equals
boolean equals(it) {
return compareTo(it) == 0;
}
otherwise you will get strange problems when you put your object in Maps and Sets. It is generally good practice to also define hashCode.

you need to override Object class equals() and hashCode() methods.
Use IDE generated code for these, it will pull all the Object attributes and creates method for you.
On eclipse IDE:
Right click on the class
Select Source
Generate hashCode() and equals()...

This is the documentation from FindBugs:
Eq: Class defines compareTo(...) and uses Object.equals()
(EQ_COMPARETO_USE_OBJECT_EQUALS)
This class defines a compareTo(...) method but inherits its equals()
method from java.lang.Object. Generally, the value of compareTo should
return zero if and only if equals returns true. If this is violated,
weird and unpredictable failures will occur in classes such as
PriorityQueue. In Java 5 the PriorityQueue.remove method uses the
compareTo method, while in Java 6 it uses the equals method.
From the JavaDoc for the compareTo method in the Comparable interface:
It is strongly recommended, but not strictly required that
(x.compareTo(y)==0) == (x.equals(y)). Generally speaking, any class
that implements the Comparable interface and violates this condition
should clearly indicate this fact. The recommended language is "Note:
this class has a natural ordering that is inconsistent with equals."
So it seems you need to implement the equals method thus overriding the default implementation from Object.

Related

Which problems can stem from overriding java.util.HashSets contains()-method?

I want to use a HashSet to store some objects:
public class StoredObject{
Type type; //Type is an enum
//other fields
Type getType(){return type;}
}
Now, I want to store only one StoredObject of the same Type, so I override contains() in a subclass of HashSet:
public MySet<E extends StoredObject> extends java.util.HashSet<E>{
#Override
public boolean contains(Object o) {
if(StoredObject.class.isAssignableFrom(o.getClass())) {//if o implements StoredObject
for(StoredObject s : this) {
if(s.getType() == ((StoredObject) o).getType()) return true;
}
}
return false
}
}
Before this I wanted to use HashSet and modify the equals() of StoredObject. However, the way above seems like a shorter and safer way, especially as in my case the stored objects all implement an interface and don't extend the same class.
Now my question: Is this implementation safe? I tried to search for things it could break, but did not find any. I read that overriding equals() can break Collections.
Also, does this subclass defeats the purpose of an HashSet, since it does not use the HashMap for contains()?
HashMap<Type,StoredObject> is the appropriate collection for this.
If you override equals(Object) then you must also override hashCode (it's also not a bad idea to make it implement Comparable and perhaps override toString). Use the #Override annotation to ensure you have the right parameter types and spelling - very easy to get wrong and confusing to debug.
What can go wrong?
There's a lot of methods in HashSet to override, so that's a lot of work.
More methods may be added to HashSet in future versions of Java - how are you going to look out for this?
contains should be an O(1) operation (assuming a good distribution of hash codes), but the OP implementation is O(n).
Set.equals on another Set will report incorrect results.
Note also that StoredObject.class.isAssignableFrom(o.getClass()) is better written as o instanceof StoredObject (assuming you've got isAssignableFrom the right way around).
Is this implementation safe?
Absolutely not. There are other methods on HashSet that wouldn't work correctly, e.g. add(), leaving the size of the set incorrect.
Besides, that implementation would totally ruin the performance of the contains method, making it run in O(n) instead of O(1).
If you need a Set with a definition of equality that differs from the objects natural definition as implemented by equals() and hashCode(), use a TreeSet and supply a custom Comparator.
class MySet<E extends StoredObject> extends java.util.TreeSet<E> {
public MySet() {
super(Comparator.comparing(StoredObject::getType));
}
}
I do agree with Tom Hawtin - tackline, that HashMap<Type, StoredObject> is a better option, because it allows you to get the StoredObject for a given Type, which is otherwise very difficult to do with a Set. It also allows you to check for existence given just a Type, without having to create a dummy StoredObject object for the check.

overriding the equals method from the superclass

Is a new implementation of the hashcode() method required when we override the equals() method from the superclass. The contract between equals() and hashcode() is kept.
The equals and hashCode methods will be (just like any other method) inherited from the superclass.
If those are still appropriate for the subclass you can keep this as is.
There are examples in the JDK for this:
Stack extends Vector extends AbstractList
Stack just inherits equals from Vector
Vector overrides equals from AbstractList, but only to add synchronized and then just calls super.equals.
ArrayList also extends AbstractList, but it overrides equals with an implementation that follows the same logic, but can be more efficient because it knows that at least one of the participants is an ArrayList.
But keep in mind that
you will now be comparing Super <-> Super, Super <-> Sub, Sub <-> Sub instances in all combinations with the same piece of code
things like this.getClass() might return unexpected things when this can actually be a subclass
If that causes problems in your comparison logic is up to you to decide.
Contract between equals and hashcode method need to be kept when your are going to use that class in hashing based collections, like hashmap, hashset etc which uses hashcode method to calculate the hash index and equals method to check the equality. Otherwise you can ignore hashcode. Please refer below URL for more information on equals and hashcode method overriding in hashmap.
https://www.thetechnojournals.com/2019/10/why-hashmap-key-should-be-immutable-in.html

How should I implement Comparable when equals() and hashCode() are not defined?

The code my team is working on has several classes where equals and hashCode are not defined in the class hierarchy. We'd like to implement Comparable such that compareTo is consistent with equals using hashCode, like so:
class MyClass implements Comparable<MyClass>
{
private String myProperty;
// Other properties, etc.
....
public int compareTo(MyClass obj) {
// Natural ordering comparisons
...
// Reach here if natural ordering properties are equivalent
return new Integer(this.hashCode()).compareTo(new Integer(obj.hashCode());
}
}
Is this considered a valid means of implementing Comparable? Are there any pitfalls with using the default hashCode implementation that I should be aware of?
UPDATE: The behavior we're striving for is as follows:
The class properties are compared first, in a natural ordering we define.
If a given property for the two objects are equivalent, we move on to the next one in the ordering.
If all properties are equivalent, we return 0 only if this.equals(obj).
Yes this is a valid way. Apparently you want a fixed ordering for objects which are equal on other values (am I right? You did not explain your aim with the hashcode usage here).
The only thing i would do is copy the java code of Integer.compareTo() in your compareTo method, so you do not have to create 2 Integers for every comparison.
No, This is not the valid means of implementing Comparable. Because , suppose your all natural ordering comparison for two different objects of MyClass within equals method comes true , after that when hashcode of two objects are compared it would return false . This is so because in this case hashcode method of Object class would be called by default(as you have not provided your own hashcode method), Which will be different for different objects. Hence the two objects of MyClass will never be equal no matter if all natural ordering comparison comes out to be true.

Why was the equals() method in java.util.Comparator made abstract?

Why was the equals() method in java.util.Comparator made abstract, if it is already implemented in the Object class?
First of all, it's worth noting that the method is not "made abstract". If you implement Comparator<T> without implementing equals(), your code will compile. Your class will simply use the implementation provided by Object.
As to why re-declare the method, this is done because the contract on Comparator<T>.equals() is more stringent than the contract on Object.equals(). This is explained in the documentation:
Additionally, this method can return true only if the specified object is also a comparator and it imposes the same ordering as this comparator. Thus, comp1.equals(comp2) implies that sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2)) for every object reference o1 and o2.
If Comparator did not override equals(), there would be no good way to specify that its contract on equals() is different from Object's.

How default .equals and .hashCode will work for my classes?

Say I have my own class
public class MyObj { /* ... */ }
It has some attributes and methods. It DOES NOT implement equals, DOES NOT implement hashCode.
Once we call equals and hashCode, what are the default implementations? From Object class? And what are they? How the default equals will work? How the default hashCode will work and what will return? == will just check if they reference to the same object, so it's easy, but what about equals() and hashCode() methods?
Yes, the default implementation is Object's (generally speaking; if you inherit from a class that redefined equals and/or hashCode, then you'll use that implementation instead).
From the documentation:
equals
The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns
true if and only if x and y refer to the same object (x == y has the value true).
hashCode
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
From Object in one of the JVM implementations:
public boolean equals(Object object) {
return this == object;
}
public int hashCode() {
return VMMemoryManager.getIdentityHashCode(this);
}
In both cases it's just comparing the memory addresses of the objects in question.
There are default implementations of equals() and hashCode() in Object. If you don't provide your own implementation, those will be used. For equals(), this means an == comparison: the objects will only be equal if they are exactly the same object. For hashCode(), the Javadoc has a good explanation.
For more information, see Effective Java, Chapter 3 (pdf), item 8.
Yes, from Object class since your class extends Object implicitly. equals simply returns this == obj. hashCode implementation is native. Just a guess - it returns the pointer to the object.
If you do not provide your own implementation, one derived from Object would be used. It is OK, unless you plan to put your class instances into i.e. HashSet (any collection that actually use hashCode() ), or something that need to check object's equality (i.e. HashSet's contains() method). Otherwise it will work incorrectly, if that's what you are asking for.
It is quite easy to provide your own implementation of these methods thanks to HashCodeBuilder and EqualsBuilder from Apache Commons Lang.
IBM's developerworks says:
Under this default implementation, two
references are equal only if they
refer to the exact same object.
Similarly, the default implementation
of hashCode() provided by Object is
derived by mapping the memory address
of the object to an integer value.
However, to be sure of the exact implementation details for a particular vendor's Java version it's probably best to look as the source (if it's available)

Categories

Resources