FindBugs - how to solve EQ_COMPARETO_USE_OBJECT_EQUALS - java

I am clueless here...
1: private static class ForeignKeyConstraint implements Comparable<ForeignKeyConstraint> {
2: String tableName;
3: String fkFieldName;
4:
5: public int compareTo(ForeignKeyConstraint o) {
6: if (this.tableName.compareTo(o.tableName) == 0) {
7: return this.fkFieldName.compareTo(o.fkFieldName);
8: }
9: return this.tableName.compareTo(o.tableName);
10: }
11: }
In line 6 I get from FindBugs: Bug: net.blabla.SqlFixer$ForeignKeyConstraint defines compareTo(SqlFixer$ForeignKeyConstraint) and uses Object.equals()
Link to definition
I don't know how to correct this.

This errors means that you're not overriding equals in ForeignKeyConstraint (and thus inheriting the equals from Object) so the following is not true (from the javadoc of compareTo):
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."
To fix the FindBugs check, override equals - and hashCode - if it makes sense which is generally the case (or exclude the check for this class and document that your class violates this condition using the suggested note).

It's telling you that there's the potential for compareTo() and equals() to disagree. And they should, really, never disagree.
The equals() method is being inherited from java.lang.Object, which by default checks to see if two objects are the same instance. Your compareTo method is comparing objects are based on tableName and fkFieldName. So you'll potentially find yourself in a situation where compareTo states that two objects are the same (because tableName and fkFieldName match), but equals states they are different (because they're different instances).
There are a few java APIs that depend on compareTo and equals being consistant; this is part of the java language and is considered a core language contract. Ideally implement an equals (and hashcode) method to check for equality based on tableName and fkFieldName.

You can solve it by implementing an equals() method. Refer to the FindBugs definition:
"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."
"It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y))."
Another example is the TreeSet. It implements equality checks by invoking compareTo, and a compareTo implementation that is inconsistent with equals makes the TreeSet violate the contract of the Set interface, which might lead to program malfunction.

Have you tried overriding the equals method as well in SqlFixer.ForeignKeyConstraint?
I believe the basis of the warning is that, as stated in the definition, strange things can happen if you override compareTo and not equals.
For more information check out Joshua Bloch's Effective Java, 2nd Edition. Item 12 goes more in depth about the ins and outs of implementing Comparable and some of the things to look out for.

Findbugs is happy with:
public int compareTo(ForeignKeyConstraint o) {
if (this.equals(o)) {
return 0;
} else if (this.tableName.equals(o.tableName)) {
// fkFieldName must be different
return this.fkFieldName.compareTo(o.fkFieldName);
} else {
// tableName must be different
return this.tableName.compareTo(o.tableName);
}
}
#Override
public equals() {
...
}
#Override
public int hashCode() {
...
}

Related

Which is the best way to compare enums in Java? Why I should use == and not .equals() method? [duplicate]

I know that Java enums are compiled to classes with private constructors and a bunch of public static members. When comparing two members of a given enum, I've always used .equals(), e.g.
public useEnums(SomeEnum a)
{
if(a.equals(SomeEnum.SOME_ENUM_VALUE))
{
...
}
...
}
However, I just came across some code that uses the equals operator == instead of .equals():
public useEnums2(SomeEnum a)
{
if(a == SomeEnum.SOME_ENUM_VALUE)
{
...
}
...
}
Which operator is the one I should be using?
Both are technically correct. If you look at the source code for .equals(), it simply defers to ==.
I use ==, however, as that will be null safe.
Can == be used on enum?
Yes: enums have tight instance controls that allows you to use == to compare instances. Here's the guarantee provided by the language specification (emphasis by me):
JLS 8.9 Enums
An enum type has no instances other than those defined by its enum constants.
It is a compile-time error to attempt to explicitly instantiate an enum type. The final clone method in Enum ensures that enum constants can never be cloned, and the special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization. Reflective instantiation of enum types is prohibited. Together, these four things ensure that no instances of an enum type exist beyond those defined by the enum constants.
Because there is only one instance of each enum constant, it is permissible to use the == operator in place of the equals method when comparing two object references if it is known that at least one of them refers to an enum constant. (The equals method in Enum is a final method that merely invokes super.equals on its argument and returns the result, thus performing an identity comparison.)
This guarantee is strong enough that Josh Bloch recommends, that if you insist on using the singleton pattern, the best way to implement it is to use a single-element enum (see: Effective Java 2nd Edition, Item 3: Enforce the singleton property with a private constructor or an enum type; also Thread safety in Singleton)
What are the differences between == and equals?
As a reminder, it needs to be said that generally, == is NOT a viable alternative to equals. When it is, however (such as with enum), there are two important differences to consider:
== never throws NullPointerException
enum Color { BLACK, WHITE };
Color nothing = null;
if (nothing == Color.BLACK); // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException
== is subject to type compatibility check at compile time
enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };
if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT); // DOESN'T COMPILE!!! Incompatible types!
Should == be used when applicable?
Bloch specifically mentions that immutable classes that have proper control over their instances can guarantee to their clients that == is usable. enum is specifically mentioned to exemplify.
Item 1: Consider static factory methods instead of constructors
[...] it allows an immutable class to make the guarantee that no two equal instances exist: a.equals(b) if and only if a==b. If a class makes this guarantee, then its clients can use the == operator instead of the equals(Object) method, which may result in improved performance. Enum types provide this guarantee.
To summarize, the arguments for using == on enum are:
It works.
It's faster.
It's safer at run-time.
It's safer at compile-time.
Using == to compare two enum values works, because there is only one object for each enum constant.
On a side note, there is actually no need to use == to write null-safe code, if you write your equals() like this:
public useEnums(final SomeEnum a) {
if (SomeEnum.SOME_ENUM_VALUE.equals(a)) {
…
}
…
}
This is a best practice known as Compare Constants From The Left that you definitely should follow.
As others have said, both == and .equals() work in most cases. The compile time certainty that you're not comparing completely different types of Objects that others have pointed out is valid and beneficial, however the particular kind of bug of comparing objects of two different compile time types would also be found by FindBugs (and probably by Eclipse/IntelliJ compile time inspections), so the Java compiler finding it doesn't add that much extra safety.
However:
The fact that == never throws NPE in my mind is a disadvantage of ==. There should hardly ever be a need for enum types to be null, since any extra state that you may want to express via null can just be added to the enum as an additional instance. If it is unexpectedly null, I'd rather have a NPE than == silently evaluating to false. Therefore I disagree with the it's safer at run-time opinion; it's better to get into the habit never to let enum values be #Nullable.
The argument that == is faster is also bogus. In most cases you'll call .equals() on a variable whose compile time type is the enum class, and in those cases the compiler can know that this is the same as == (because an enum's equals() method can not be overridden) and can optimize the function call away. I'm not sure if the compiler currently does this, but if it doesn't, and turns out to be a performance problem in Java overall, then I'd rather fix the compiler than have 100,000 Java programmers change their programming style to suit a particular compiler version's performance characteristics.
enums are Objects. For all other Object types the standard comparison is .equals(), not ==. I think it's dangerous to make an exception for enums because you might end up accidentally comparing Objects with == instead of equals(), especially if you refactor an enum into a non-enum class. In case of such a refactoring, the It works point from above is wrong. To convince yourself that a use of == is correct, you need to check whether value in question is either an enum or a primitive; if it was a non-enum class, it'd be wrong but easy to miss because the code would still compile. The only case when a use of .equals() would be wrong is if the values in question were primitives; in that case, the code wouldn't compile so it's much harder to miss. Hence, .equals() is much easier to identify as correct, and is safer against future refactorings.
I actually think that the Java language should have defined == on Objects to call .equals() on the left hand value, and introduce a separate operator for object identity, but that's not how Java was defined.
In summary, I still think the arguments are in favor of using .equals() for enum types.
I prefer to use == instead of equals:
Other reason, in addition to the others already discussed here, is you could introduce a bug without realizing it. Suppose you have this enums which is exactly the same but in separated pacakges (it's not common, but it could happen):
First enum:
package first.pckg
public enum Category {
JAZZ,
ROCK,
POP,
POP_ROCK
}
Second enum:
package second.pckg
public enum Category {
JAZZ,
ROCK,
POP,
POP_ROCK
}
Then suppose you use the equals like next in item.category which is first.pckg.Category but you import the second enum (second.pckg.Category) instead the first without realizing it:
import second.pckg.Category;
...
Category.JAZZ.equals(item.getCategory())
So you will get allways false due is a different enum although you expect true because item.getCategory() is JAZZ. And it could be be a bit difficult to see.
So, if you instead use the operator == you will have a compilation error:
operator == cannot be applied to "second.pckg.Category", "first.pckg.Category"
import second.pckg.Category;
...
Category.JAZZ == item.getCategory()
tl;dr
Another option is the Objects.equals utility method.
Objects.equals( thisEnum , thatEnum )
Objects.equals for null-safety
equals operator == instead of .equals()
Which operator is the one I should be using?
A third option is the static equals method found on the Objects utility class added to Java 7 and later.
Example
Here’s an example using the Month enum.
boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ; // Returns `false`.
Benefits
I find a couple benefits to this method:
Null-safety
Both null ➙ true
Either null ➙ false
No risk of throwing NullPointerException
Compact, readable
How it works
What is the logic used by Objects.equals?
See for yourself, from the Java 10 source code of OpenJDK:
return
( a == b )
||
(
a != null
&&
a.equals( b )
)
;
Here is a crude timing test to compare the two:
import java.util.Date;
public class EnumCompareSpeedTest {
static enum TestEnum {ONE, TWO, THREE }
public static void main(String [] args) {
Date before = new Date();
int c = 0;
for(int y=0;y<5;++y) {
for(int x=0;x<Integer.MAX_VALUE;++x) {
if(TestEnum.ONE.equals(TestEnum.TWO)) {++c;}
if(TestEnum.ONE == TestEnum.TWO){++c;}
}
}
System.out.println(new Date().getTime() - before.getTime());
}
}
Comment out the IFs one at a time. Here are the two compares from above in disassembled byte-code:
21 getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
24 getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
27 invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28]
30 ifeq 36
36 getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
39 getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
42 if_acmpne 48
The first (equals) performs a virtual call and tests the return boolean from the stack. The second (==) compares the object addresses directly from the stack. In the first case there is more activity.
I ran this test several times with both IFs one at a time. The "==" is ever so slightly faster.
One of the Sonar rules is Enum values should be compared with "==". The reasons are as follows:
Testing equality of an enum value with equals() is perfectly valid because an enum is an Object and every Java developer knows == should not be used to compare the content of an Object. At the same time, using == on enums:
provides the same expected comparison (content) as equals()
is more null-safe than equals()
provides compile-time (static) checking rather than runtime checking
For these reasons, use of == should be preferred to equals().
Last but not least, the == on enums is arguably more readable (less verbose) than equals().
In case of enum both are correct and right!!
Using anything other than == to compare enum constants is nonsense. It's like comparing class objects with equals – don't do it!
However, there was a nasty bug (BugId 6277781) in Sun JDK 6u10 and earlier that might be interesting for historical reasons. This bug prevented proper use of == on deserialized enums, although this is arguably somewhat of a corner case.
Enums are classes that return one instance (like singletons) for each enumeration constant declared by public static final field (immutable) so that == operator could be used to check their equality rather than using equals() method
The reason enums work easily with == is because each defined instance is also a singleton. So identity comparison using == will always work.
But using == because it works with enums means all your code is tightly coupled with usage of that enum.
For example: Enums can implement an interface. Suppose you are currently using an enum which implements Interface1. If later on, someone changes it or introduces a new class Impl1 as an implementation of same interface. Then, if you start using instances of Impl1, you'll have a lot of code to change and test because of previous usage of ==.
Hence, it's best to follow what is deemed a good practice unless there is any justifiable gain.
Just one thing to add to all the other excellent answers. When you use a simple lambda I do prefer equals over ==, because you can use method referencing.
Consider following lambdas:
Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(e -> e == SomeEnum.B);
Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(e -> e.equals(SomeEnum.B));
The later can be converted to:
Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(SomeEnum.B::equals));
I want to complement polygenelubricants answer:
I personally prefer equals(). But it lake the type compatibility check. Which I think is an important limitation.
To have type compatibility check at compilation time, declare and use a custom function in your enum.
public boolean isEquals(enumVariable) // compare constant from left
public static boolean areEqual(enumVariable, enumVariable2) // compare two variable
With this, you got all the advantage of both solution: NPE protection, easy to read code and type compatibility check at compilation time.
I also recommend to add an UNDEFINED value for enum.
In short, both have pros and cons.
On one hand, it has advantages to use ==, as described in the other answers.
On the other hand, if you for any reason replace the enums with a different approach (normal class instances), having used == bites you. (BTDT.)

Java Null Object that is used in Hash Tables and Comparables

I have an object that represents an UNKNOWN value, or "Null object".
As in SQL, this object should never be equal to anything, including another UNKNOWN, so (UNKNOWN == UNKNOWN) -> false.
The object is used, however, in hashtables and the type is Comparable, so I created a class as follows:
public class Tag implements Comparable {
final static UNKNOWN = new Tag("UNKNOWN");
String id;
public Tag(String id) {
this.id = id;
}
public int hashCode(){
return id.hashCode();
}
public String toString(){
return id;
}
public boolean equals(Object o){
if (this == UNKNOWN || o == UNKNOWN || o == null || !(o instanceof Tag))
return false;
return this.id.equals(((Tag)o).id);
}
public int compareTo(Tag o){
if (this == UNKNOWN)
return -1;
if (o == UNKNOWN || o == null)
return 1;
return this.id.compareTo(o.id);
}
}
But now compareTo() seems "inconsistent"?
Is there a better way to implement compareTo()?
The documentation for compareTo mentions this situation:
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."
Therefore, if you want your object to be Comparable and yet still not allow two UNKNOWN objects to be equal via the equals method, you must make your compareTo "Inconsistent with equals."
An appropriate implementation would be:
public int compareTo(Tag t) {
return this.id.compareTo(t.id);
}
Otherwise, you could make it explicit that UNKNOWN values in particular are not Comparable:
public static boolean isUnknown(Tag t) {
return t == UNKNOWN || (t != null && "UNKNOWN".equals(t.id));
}
public int compareTo(Tag t) {
if (isUnknown(this) || isUnknown(t)) {
throw new IllegalStateException("UNKNOWN is not Comparable");
}
return this.id.compareTo(t.id);
}
You're correct that your compareTo() method is now inconsistent. It violates several of the requirements for this method. The compareTo() method must provide a total order over the values in the domain. In particular, as mentioned in the comments, a.compareTo(b) < 0 must imply that b.compareTo(a) > 0. Also, a.compareTo(a) == 0 must be true for every value.
If your compareTo() method doesn't fulfil these requirements, then various pieces of the API will break. For example, if you sort a list containing an UNKNOWN value, then you might get the dreaded "Comparison method violates its general contract!" exception.
How does this square with the SQL requirement that null values aren't equal to each other?
For SQL, the answer is that it bends its own rules somewhat. There is a section in the Wikipedia article you cited that covers the behavior of things like grouping and sorting in the presence of null. While null values aren't considered equal to each other, they are also considered "not distinct" from each other, which allows GROUP BY to group them together. (I detect some specification weasel wording here.) For sorting, SQL requires ORDER BY clauses to have additional NULLS FIRST or NULLS LAST in order for sorting with nulls to proceed.
So how does Java deal with IEEE 754 NaN which has similar properties? The result of any comparison operator applied to NaN is false. In particular, NaN == NaN is false. This would seem to make it impossible to sort floating point values, or to use them as keys in maps. It turns out that Java has its own set of special cases. If you look at the specifications for Double.compareTo() and Double.equals(), they have special cases that cover exactly these situations. Specifically,
Double.NaN == Double.NaN // false
Double.valueOf(Double.NaN).equals(Double.NaN) // true!
Also, Double.compareTo() is specified so that it considers NaN equal to itself (it is consistent with equals) and NaN is considered larger than every other double value including POSITIVE_INFINITY.
There is also a utility method Double.compare(double, double) that compares two primitive double values using these same semantics.
These special cases let Java sorting, maps, and so forth work perfectly well with Double values, even though this violates IEEE 754. (But note that primitive double values do conform to IEEE 754.)
How should this apply to your Tag class and its UNKNOWN value? I don't think you need to follow SQL's rules for null here. If you're using Tag instances in Java data structures and with Java class libraries, you'd better make it conform to the requirements of the compareTo() and equals() methods. I'd suggest making UNKNOWN equal to itself, to have compareTo() be consistent with equals, and to define some canonical sort order for UNKNOWN values. Usually this means sorting it higher than or lower than every other value. Doing this isn't terribly difficult, but it can be subtle. You need to pay attention to all the rules of compareTo().
The equals() method might look something like this. Fairly conventional:
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
return obj instanceof Tag && id.equals(((Tag)obj).id);
}
Once you have this, then you'd write compareTo() in a way that relies on equals(). (That's how you get the consistency.) Then, special-case the unknown values on the left or right-hand sides, and finally delegate to comparison of the id field:
public int compareTo(Tag o) {
if (this.equals(o)) {
return 0;
}
if (this.equals(UNKNOWN)) {
return -1;
}
if (o.equals(UNKNOWN)) {
return 1;
}
return id.compareTo(o.id);
}
I'd recommend implementing equals(), so that you can do things like filter UNKNOWN values of a stream, store it in collections, and so forth. Once you've done that, there's no reason not to make compareTo consistent with equals. I wouldn't throw any exceptions here, since that will just make standard libraries hard to use.
The simple answer is: you shouldn't.
You have contradiction requirements here. Either your tag objects have an implicit order (that is what Comparable expresses) OR you can have such "special" values that are not equal to anything, not even themselves.
As the other excellent answer and the comments point out: yes, you can somehow get there; for example by simply allowing for a.compare(b) < 0 and b.compare(a) < 0 at the same time; or by throwing an exception.
But I would simply be really careful about this. You are breaking a well established contract. And the fact that some javadoc says: "breaking the contract is OK" is not the point - breaking that contract means that all the people working on this project have to understand this detail.
Coming from there: you could go forward and simply throw an exception within compareTo() if a or b are UNKNOWN; by doing so you make at least clear that one shouldn't try to sort() a List<Tag> for example. But hey, wait - how would you find out that UNKNOWN is present in your list? Because, you know, UNKNOWN.equals(UNKNOWN) returns false; and contains() is using equals.
In essence: while technically possible, this approach causes breakages wherever you go. Meaning: the fact that SQL supports this concept doesn't mean that you should force something similar into your java code. As said: this idea is very much "off standards"; and is prone to surprise anybody looking at it. Aka "unexpected behavior" aka bugs.
A couple seconds of critical thinking:
There is already a null in Java and you can not use it as a key for a reason.
If you try and use a key that is not equal to anything else including
itself you can NEVER retrieve the value associated with that key!

Effective java item 9: overriding hashcode example

I was reading Effective Java Item 9 and decided to run the example code by myself. But it works slightly different depending on how I insert a new object that I don't understand what exactly is going on inside. The PhoneNumber class looks:
public class PhoneNumber {
private final short areaCode;
private final short prefix;
private final short lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
this.areaCode = (short)areaCode;
this.prefix = (short) prefix;
this.lineNumber = (short)lineNumber;
}
#Override public boolean equals(Object o) {
if(o == this) return true;
if(!(o instanceof PhoneNumber)) return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode;
}
}
Then according to the book and as is when I tried,
public static void main(String[] args) {
HashMap<PhoneNumber, String> phoneBook = new HashMap<PhoneNumber, String>();
phoneBook.put(new PhoneNumber(707,867,5309), "Jenny");
System.out.println(phoneBook.get(new PhoneNumber(707,867,5309)));
}
This prints "null" and it's explained in the book because HashMap has an optimization that caches the hash code associated with each entry and doesn't check for object equality if the hash codes don't match. It makes sense to me. But when I do this:
public static void main(String[] args) {
PhoneNumber p1 = new PhoneNumber(707,867,5309);
phoneBook.put(p1, "Jenny");
System.out.println(phoneBook.get(new PhoneNumber(707,867,5309)));
}
Now it returns "Jenny". Can you explain why it didn't fail in the second case?
The experienced behaviour might depend on the Java version and vendor that was used to run the application, because since the general contract of Object.hashcode() is violated, the result is implementation dependent.
A possible explanation (taking one possible implementation of HashMap):
The HashMap class in its internal implementation puts objects (keys) in different buckets based on their hashcode. When you query an element or you check if a key is contained in the map, first the proper bucket is looked for based on the hashcode of the queried key. Inside the bucket objects are checked in a sequencial way, and inside a bucket only the equals() method is used to compare elements.
So if you do not override Object.hashcode() it will be indeterministic if 2 different objects produce default hashcodes which may or may not determine the same bucket. If by any chance they "point" to the same bucket, you will still be able to find the key if the equals() method says they are equal. If by any chance they "point" to 2 different buckets, you will not find the key even if equals() method says they are equal.
hashcode() must be overriden to be consistent with your overridden equals() method. Only in this case it is guaranteed the proper, expected and consistent working of HashMap.
Read the javadoc of Object.hashcode() for the contract that you must not violate. The main point is that if equals() returns true for another object, the hashcode() method must return the same value for both of these objects.
Can you explain why it didn't fail in the second case?
In a nutshell, it is not guaranteed to fail. The two objects in the second example could end up having the same hash code (purely by coincidence or, more likely, due to compiler optimizations or due to how the default hashCode() works in your JVM). This would lead to the behaviour you describe.
For what it's worth, I cannot reproduce this behaviour with my compiler/JVM.
In your case by coincidence JVM was able to find the same hashCode for both object. When I ran your code, in my JVM it gave null for both the case. So your problem is because of JVM not the code.
It is better to override hashCode() each and every time when you override equils() method.
I haven't read Effective Java, I read SCJP by Kathy Sierra. So if you need more details then you can read this book. It's nice.
Your last code snipped does not compile because you haven't declared phoneBook.
Both main methods should work exactly the same. There is a 1 in 16 chance that it will print Jenny because a newly crated HashMap has a default size of 16. In detail that means that only the lower 4 bits of the hashCode will be checked. If they equal the equal method is used.

Necessity of Identity Test in equals method

EDIT: Heart of the Matter
When would an Identity Test pass, when the rest of a traditional equals method would fail? Is this added just to save the time of doing extra work?
Original Post
I am utilizing the CompareToBuilder from org.apache.commons.lang3.builder.CompareToBuilder in a class I am testing. I notice that the EqualsBuilder requires the following code to be explicitly called BEFORE calling the equals builder.
if (obj == this) { return true; } // Identity test
Such logic also appears in the Eclipse auto-generated equals method. I am attempting to use DRY methodology by having my equals method simply call my compareTo method and test equivalence to 0.
One question is whether I need to include the above code in my equals method, add it to my compareTo method or if it is already covered by CompareToBuilder. I notice that CompareToBuilder checks the equivalence of the parameters passed but does not recieve any direct references to the original lhs (this) and rhs (Object obj). That leads me to think that it is an oversight I should correct in my compareTo method.
My largest issue is that I cannot seem to devise a potential test case in which obj == this but this.compareTo(obj) != 0. Only thing that comes to mind is in an improperly implemented compareTo, sending in obj with one of its instance variables null could return a non-zero number if it is not first checked to see if the corresponding variable in this is also null. (Ran into this yesterday).
Sample equals method:
#Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof MyClass)
return this.compareTo((MyClass)obj) == 0;
return false;
}
Sample compareTo method
#Override
public int compareTo(MyClass other) {
return new CompareToBuilder()
.append(this.getParam1(), other.getParam1())
.append(this.getParam2(), other.getParam2())
.toComparison();
}
Looking in the documentation for Comparable, compareTo() requires that
x.compareTo(y) == -y.compareTo(x)
Therefore, if x == y, x.compareTo(x) == -x.compareTo(x) and thus x.compareTo(x) == 0. So the only way to make x == y && x.compareTo(y) != 0 is to break the compareTo() contract.
Note that, in my understanding, the obj == this test tends to be added to equals() methods as an optimization to keep from evaluating a deep comparison when unnecessary and does not affect the result of the call. (See Effective Java 2nd Ed, Item 8.) I assume the same holds true for the compareTo() method here.
When would an Identity Test pass, when the rest of a traditional equals method would fail? Is this added just to save the time of doing extra work?
The short answer is: never.
The identity test is a cheap optimization intended as a quick way out of the rest of the equals() method. In fact, Joshua Bloch recommends that every equals() method begin with just such a test as an optimization (Effective Java, 2nd Ed.).
Mr. Bloch recommends the following general structure for equals():
Use the == operator to check if the argument is a reference to this object. If it is, return true.
Use the instanceof operator to check if the argument is of the correct type. If it is not, then return false.
Cast the argument to the correct type.
For each significant field in the class, check if that field of the argument matches the corresponding field of this object.
and etc.
When would an identity test pass when a traditional equals fail?
Technically, if "traditional" also means "naive", then an object graph with a loop in it (e.g. A has an ivar that points to B which has an ivar that points to A) would pass the identity test, but a traditional equals implementation would go into an infinite loop.
I'm not sure if the Apache library would protect against this.

subtleties of dealing with equals and hashCode in a Java interface

I'm implementing a value object for these interfaces:
interface FooConsumer
{
public void setFoo(FooKey key, Foo foo);
public Foo getFoo(FooKey key);
}
// intent is for this to be a value object with equivalence based on
// name and serial number
interface FooKey
{
public String getName();
public int getSerialNumber();
}
and from what I've read (e.g. in Enforce "equals" in an interface and toString(), equals(), and hashCode() in an interface) it looks like the recommendation is to provide an abstract base class, e.g.
abstract class AbstractFooKey
{
final private String name;
final private int serialNumber
public AbstractFooKey(String name, int serialNumber)
{
if (name == null)
throw new NullPointerException("name must not be null");
this.name = name;
this.serialNumber = serialNumber;
}
#Override public boolean equals(Object other)
{
if (other == this)
return true;
if (!(other instanceof FooKey))
return false;
return getName().equals(other.getName()
&& getSerialNumber() == other.getSerialNumber()
&& hashCode() == other.hashCode(); // ***
}
#Override public int hashCode()
{
return getName().hashCode() + getSerialNumber()*37;
}
}
My question is about the last bit I added here, and how to deal with the situation where AbstractFooKey.equals(x) is called with a value for x that is an instance of a class that implements FooKey but does not subclass AbstractFooKey. I'm not sure how to handle this; on the one hand I feel like the semantics of equality should just depend on the name and serialNumber being equal, but it appears like the hashCodes have to be equal as well in order to satisfy the contract for Object.equals().
Should I be:
really lax and just forget about the line marked ***
lax and keep what I have
return false from equals() if the other object is not an AbstractFooKey
be really strict and get rid of the interface FooKey and replace it with a class that is final?
something else?
Document the required semantics as part of the contract.
Ideally you'd actually have a single implementation which is final, which kind of negates the need of an interface for this particular purpose. You may have other reasons for wanting an interface for the type.
The contract requirements of Object is actually from hashCode: If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
You don't need to include hashCode in the equals computation, rather you need to include all properties involved in equals in the hashCode calculation. In this case I'd simply compare serialNumber and name in both equals and hashCode.
Keep it simple unless you have a real reason to complicate it.
Start with a final, immutable class.
If you need an interface, create one to match, and document the semantics and default implementation.
For the equals and hashmap, there are strict contracts:
Reflexive - It simply means that the object must be equal to itself, which it would be at any given instance; unless you intentionally override the equals method to behave otherwise.
Symmetric - It means that if object of one class is equal to another class object, the other class object must be equal to this class object. In other words, one object can not unilaterally decide whether it is equal to another object; two objects, and consequently the classes to which they belong, must bilaterally decide if they are equal or not. They BOTH must agree.
Transitive - It means that if the first object is equal to the second object and the second object is equal to the third object; then the first object is equal to the third object. In other words, if two objects agree that they are equal, and follow the symmetry principle, one of them can not decide to have a similar contract with another object of different class. All three must agree and follow symmetry principle for various permutations of these three classes.
Consistent - It means that if two objects are equal, they must remain equal as long as they are not modified. Likewise, if they are not equal, they must remain non-equal as long as they are not modified. The modification may take place in any one of them or in both of them.
null comparison - It means that any instantiable class object is not equal to null, hence the equals method must return false if a null is passed to it as an argument. You have to ensure that your implementation of the equals method returns false if a null is passed to it as an argument.
Contract for hashCode():
Consistency during same execution - Firstly, it states that the hash code returned by the hashCode method must be consistently the same for multiple invocations during the same execution of the application as long as the object is not modified to affect the equals method.
Hash Code & Equals relationship - The second requirement of the contract is the hashCode counterpart of the requirement specified by the equals method. It simply emphasizes the same relationship - equal objects must produce the same hash code. However, the third point elaborates that unequal objects need not produce distinct hash codes.
(From: Technofundo: Equals and Hash Code)
However, using instanceof in equals is not the right thing to do. Joshua Bloch detailed this in Effective Java, and your concerns regarding the validity of your equals implementation is valid. Most likely, problems arising from using instanceof are going to violate the transitivity part in the contract when used in connection with descendants of the base class - unless the equals function is made final.
(Detailed a bit better than I could ever do here: Stackoverflow: Any reason to prefer getClass() over instanceof when generating .equals()?)
Also read:
Java API equals contract
Java API hashCode contract
If the equality of a FooKey is such that two FooKeys with the same name and serial numbers are considered to be equal then you can remove the line in the equals() clause that compares the hashcodes.
Or you could leave it in, it does not really matter assuming that all implementors of the FooKey interface have a correct implementation of equals and gethashcode but I would recommend removing it since otherwise a reader of the code could get the impression that it is there because it makes a difference when in reality it does not.
You can also get rid of the '*37' in the gethashcode method, it is unlikely it would contribute to better hashcode distribution.
In terms of your question 3, I would say no, don't do that, unless the equality contract for FooKey is not controlled by you (in which case trying to enforce an equality contract for the interface is questionable anyway)

Categories

Resources