You probably already knew that Java operator “==” is kina tricky. Because it just useless in the most cases. In Java you are dealing mainly with object, and you can not use “==” operator to compare objects, you must use .equals() method instead. As for object “==” compare object references not their actual content.

Despite that, it is possible to see such code that not only use “==” for comparing object, but it also works properly. Some parts of this code found in production could be considered as an exceptional example of programmer’s stupidity, aka shitcode.

Here is our test application. And you definitely would notice what strange look does it have.

import static java.lang.System.out;
import java.math.BigDecimal;
public class Main
{
    public static void main(String[] args) {
        // Note that our variables are objects
        Integer a = 1;
        Integer b = 666;

        out.println(a == 1);
        out.println(b == 666);

        out.println(b == new Integer(666));
        out.println(a == new Integer(1));
        out.println(new Integer(1) == new Integer(1));

        out.println(a == Integer.valueOf(1));
        out.println(b == Integer.valueOf(666));

        out.println(Integer.valueOf(666) == Integer.valueOf(666));

        BigDecimal bd = BigDecimal.valueOf(33.0);
        out.println(bd.equals(new BigDecimal("33.0")));
        out.println(bd.equals(new BigDecimal("33.00")));
        out.println(bd.equals(new BigDecimal("33")));
        out.println(bd.equals(new BigDecimal(33)));
    }
}

Let’s dive deeper into results line by line.

a == 1; // -> true
b == 666; // -> true

We have object on one side and primitive on another. We can not compare directly so Java perform unboxing - converts object to primitive. And it is OK to compare primitives using “==” operator.

b == new Integer(600+60+6); // -> false
a == new Integer(1); // -> false
new Integer(1) == new Integer(1) // -> false 

Now we have objects on both sides of our expression, and they are different regardless of values they hold. Still looks OK.

a == Integer.valueOf(1); // -> true
Integer.valueOf(1) == Integer.valueOf(1); // -> true
b == Integer.valueOf(666); // -> false
Integer.valueOf(666) == Integer.valueOf(666) // -> false

Here is where things become tricky. And what is the hell going on with valueOf method? The thing is that valueOf behavior is similar to autoboxing. Which means that Integer a = 1; is the same as Integer a = Integer.valueOf(1);.

But why some autoboxed values are comparable via “==” and other not. This is because of caching for Integer object values between -128 and 127 (inclusive). Thus, it will always return the same object (with same reference) for cached values. Value 666 is out of caching range, so it will create new object each time.

Well so if we will compare out objects with equals everything should be OK.

a.equals(1); // -> true
b.equals(666); // -> true

This is true for most cases but our shitty Java API has one little pitfall.

BigDecimal bd = BigDecimal.valueOf(33.0);
bd.equals(new BigDecimal("33.0")); // -> true
bd.equals(new BigDecimal("33.00")); // -> false
bd.equals(new BigDecimal("33")); // -> false
bd.equals(new BigDecimal(33)); // -> false

In your face programmers :) It turns out that from BigDecimal point if view “33.0” and “33.00” is different values because they have different precision. No matter if BigDecimal objects holds the same value. If precision does not mach they could not be equals.

So how we should compare BigDecimals ?

bd.compareTo(new BigDecimal("33.00")) == 0; // -> true
bd.compareTo(new BigDecimal("33")) == 0; // -> true
bd.compareTo(new BigDecimal(33)) == 0; // -> true

This is the way dudes, and it is fucking stupid. Thanks to “clever” morons who put this in Java API.

Things you should remember:

  • Autoboxing unboxing in Java could be tricky
  • Use primitives everywhere where you can
  • equals method is the only way for objects
  • BigDecimal is not a regular object use a.compareTo(b) == 0 to compare such objects