The following compares two enum values using ==:
MyEnum enum1 = blah(); // could return null
MyEnum enum2 = blahblah() // could return null
if (enum1 == enum2) {
// ...
}
But PMD gives a CompareObjectsWithEquals warning on line 3:
Use equals() to compare object references
Not sure I understand the source code for this check but thought it was OK to compare two enums using == so am wondering whether my code could be improved or the check is incorrect.
This is indeed accepted as bug:
http://sourceforge.net/p/pmd/bugs/1028/
http://sourceforge.net/p/pmd/bugs/909/
However, it seems to be tricky to catch all possible cases (quote from the newer bug):
That one is a bit tricky, as in order to determine, whether a type is
a Enum, we need type resolution.
I was able to adjust the rule to check, whether the type of the
variables is an Enum. This works only, if the Enum types are on the
"auxclasspath" of pmd, so that the type resolution can find it.
Your example in isolation would still trigger this false positive, as
PMD doesn't know what ProcessingStatus is. I verified it with
java.math.RoundingMode, which is always on the classpath and will be
resolved.
("Your example" refers to the ticket author, not the OP on Stack Overflow)
Your case might work with PMD 5, the source you linked belongs to PMD 4.
Update: The current source contains the additional check for enums:
// skip, if it is an enum
if (type0.getType() != null && type0.getType().equals(type1.getType()) && type0.getType().isEnum()) {
return data;
}
It's fine to use .equals(), because under the hoods, what happens is that the instances are compared with ==.
public final boolean equals(Object other) {
return this==other;
}
Note that this implementation of .equals() is final, which means you cannot override it in your enum.
Related
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.)
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!
Is there any tool that can warn me against the following sort of code:
if ( someClass.equals( someString ))
For example:
if ( myObject.getClass().equals( myClassName ))
Such a thing is legal Java (equals takes an Object) but will never evaluate to true (a class can never equal a String) so is almost certainly a bug.
I have checked Eclipse, FindBugs and PMD but none seem to support this feature?
Yes, IntelliJ IDEA has such an inspection that I believe is enabled by default. It flags the following:
Class<?> clazz = String.class;
if (clazz.equals("foo")) {
//...
}
With the warning:
'equals()' between objects of inconvertible types.
The inspection can be enabled/disabled through Settings->Project Settings->Inspections, then under Probable Bugs check/uncheck "'equals()' between objects of inconvertible types."
FindBugs also should catch this with the "EC: Call to equals() comparing different types" bug check. It can be integrated with Eclipse as it appears you are aware.
Neither is a silver bullet though; they can't read your mind. The best you can hope for is that it will favour false positives rather than false negatives.
This is the idea behind the IEquatable<T> interface in .NET: providing a mechanism for types to implement what I'll call strongly typed equality. There is also the IEqualityComparer<T> interface for allowing this logic to be implemented in a separate type.
According to this StackOverflow question (answered by Jon Skeet, who generally seems to know what he's talking about), there doesn't seem to be any equivalent in Java.
Of course, you can always implement such a thing yourself for your own types, but it won't do you much good with types that are part of Java's base class libraries. For compile-time detection of such issues, your best bet is some sort of analysis tool (Mark Peters indicates there is apparently one built in to IntelliJ IDEA) that can suggest to you that certain code might be suspect. In general, assuming you aren't one to ignore warnings, this ought to be good enough.
What you are checking for is not necessarily a "problem": equals() is declared in the Object class, and takes and Object as its parameter. Classes override this method, and their implementation may well allow an object of a different class to "equal" the target object.
I have done this a few times myself, for example for allowing an object to "equal" another object if the other object (say a String) matches the key field of the target:
class MyClass {
private String id;
public boolean equals(Object obj) {
// Compare as if "this" is the id field
return id.equals(obj instanceof MyClass ? ((MyClass)obj).id : obj);
}
public int hashCode() {
return id.hashCode(); // so hashCode() agrees with equals()
}
}
It's actually pretty handy, because the following code will work as desired:
List<MyClass> list = new ArrayList<MyClass>();
// collection methods will work with instances:
list.contains(someInstance);
list.remove(someInstance);
list.indexOf(someInstance);
// and with keys!
// handy if you can only get the key, for example from a web url parameter
list.contains("somekey");
list.remove("somekey");
list.indexOf("somekey");
Is there any way I can get Eclipse to highlight the use of the == operator to test String equality? I keep mistakenly using it instead of calling .equals().
I'd really like to make that into a warning and require an #SuppressWarnings annotation to remove it, in the yet-to-happen case that I actually want to compare strings for object equality.
Are there any tools can I use to help break this bad habit at edit-time?
Use a static analysis tool such as FindBugs, PMD, or CheckStyle.
There are Eclipse plugins for each, along with Ant tasks, Maven plugins, etc.
Each of these has rules relating to String equality (Findbugs rule, PMD rule, Checkstyle rule).
The obvious answer to the question has already been given, but here is a warning that is not a direct answer: obj.equals can also fail if obj is null. So you'll often have to use code like this:
if(mystr1 != null && mystr1.equals(mystr2))
because this
if(mystr1.equals(mystr2))
would fail with a NullPointerException if mystr1 is null.
Which is why, when the comparison string is a known constant, the following syntax is often used:
if("ABCDEF".equals(mystr1))
rather than
if(mystr1.equals("ABCDEF"))
For this reason, many libraries (like apache commons / lang ) provide utility functions that combine these checks:
// this is the definition of org.apache.commons.lang.StringUtils.equals(String, String)
public static boolean equals(String str1, String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
// this is the definition of org.apache.commons.lang.ObjectUtils.equals(Object, Object)
public static boolean equals(Object object1, Object object2) {
if (object1 == object2) {
return true;
}
if ((object1 == null) || (object2 == null)) {
return false;
}
return object1.equals(object2);
}
Using these methods is usually safer than plain equals, unless you know for sure that one of the two objects is not null
I disagree with previous answers - it is a bug in eclipse and you can vote for it here : https://bugs.eclipse.org/bugs/show_bug.cgi?id=39095.
Eclipse can very well warn you when you compare Strings with == as this is seldom what you wanted (or what the original author wanted).
I have always understood there to be two types of equality in Java,
value equality : uses the .equals() method to test that two objects implement an equivalence relation on non-null object references.
reference equality : uses the == operator to test that two primitive types or memory locations are equal.
The following pages describe these language fundamentals in more detail.
Wikibooks Java Programming : Java Programming/Comparing Objects
xyzws Java EE FAQ : What are the differences between the equality operator and the equals method?
Java Platform API : Javadoc for Object.equals()
Java Language Specification : Equality Operators
What none of these links explicitly specify is what should happen if two null object references are compared for value equality. The implicit assumption is that a NullPointerException should be thrown but this is not what is done by the ObjectUtils.equals() method, which might be considered a best practice utility method.
What worries me is that Apache Commons seems to have effectively introduced a third measure of equality into Java by the back door and that the already confusing state of affairs might have been made greatly more complex. I call it a third measure of equality because it attempts to test for value equality and when that fails it falls back to testing for reference equality. The Apache Commons equality test has many similarities with the value equality and reference equality but is also distinctly different.
Am I right to be concerned and to want to avoid using the ObjectUtils.equals() where ever possible?
Is there an argument for claiming that ObjectUtils.equals() provides a useful union of the other two measures of equality?
Chosen Answer
There doesn't seem to be a consensus opinion on this question but I decided to mark Bozho's as correct because he best drew my attention to what I now see as the greatest problem with null-safe equals checks. We should all be writing fail-fast code that addresses the root cause of why two null objects are being compared for value equality rather than trying to sweep the problem under the carpet.
Here's the code of ObjectUtils.equals(..):
public static boolean equals(Object object1, Object object2) {
if (object1 == object2) {
return true;
}
if ((object1 == null) || (object2 == null)) {
return false;
}
return object1.equals(object2);
}
ObjecUtils docs state clearly that objects passed can be null.
Now on the matter whether true should be returned if you compare two nulls. In my opinion - no, because:
when you compare two objects, you are probably going to do something with them later on. This will lead to a NullPointerException
passing two nulls to compare means that they got from somewhere instead of "real" objects, perhaps due to some problem. In that case comparing them alone is wrong - the program flow should have halted before that.
In a custom library we're using here we have a method called equalOrBothNull() - which differs from the equals method in this utility in the null comparison.
Am I right to be concerned and to want
to avoid using the
ObjectUtils.equals() where ever
possible?
No. What you need to consider equals depends on your requirements. And wanting to consider two nulls equal and any non-null unequal to a null without having to deal with NullPointerExceptions is a very, very common requirement (e.g. when you want to fire value-change events from a setter).
Actually, it's how equals() in general should work, and typically, half of that behvaiour is implemented (the API doc of Object.equals() states "For any non-null reference value x, x.equals(null) should return false.") - that it doesn't work the other way round is mainly due to technical restrictions (the language was designed without multiple dispatch to be simpler).
If you are concerned about this then you could either 1) not use this method 2) write your own to wrap it
public class MyObjectUtils {
public static boolean equals(Object obj1, Object obj2) {
return obj1 != null && obj2 != null && ObjectUtils.equals(obj1, obj2);
}
}
To me it seems weird to allow for null to be equals to null, but this doesn't seem like a large problem. For the most part, I wouldn't expect my application to even get into code paths that involve equality tests if one or more objects are null.