Java has primitive types and reference types. The difference is that, the primitive types are not treated as objects, while reference types are almost always objects. Depending on this, there are many places where strictly reference types are required. For example Collections. When we want to create a list of booleans, we must code as
List<Boolean>
The important part here is the Boolean class. Although our intent is to use the boolean primitive type, Collections directs us to use the Boolean wrapper class. This has important consequences. Somehow, we need to transform primitive types back and forth to the wrapper class. Thankfully, from version 5 and on, Java can do this automatically if certain conditions hold. For other cases, Java presents different constructors and static methods.
In the case of primitive types, the boolean is very simple. There only exists true and false. The comparison between two boolean values can simply be done by the == binary operator. However, the wrapper class Boolean complicates the issue.
Creating a Boolean
The use of == with primitive boolean type has a very strong influence on using it with Boolean objects. Long time Java developers know that applying == operator for comparing objects should be avoided. However, in the case of Boolean class, there are only two Boolean.TRUE
and Boolean.FALSE
static objects. It is so tempting to type == in place of equals()
. How error-prone can it be to use it for a class that only needs two objects? In fact, it can cause headaches.
When we look at the Boolean class, we see two constructors. One for converting from a boolean value, and one for converting from a String. There is a very important effect of these constructors. As their purpose of existence, they CREATE (or in Java doc terms ALLOCATE) new objects. To see what they really do, I have read the source code. To get those, we must download JDK. In macOS, we can find the Java source codes under
/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/
There is a src.zip file. Java creators make our lives easier by mirroring the package class locations to source files. Therefore, to get the Boolean.java file, we look for java/lang folders. Both of the constructors are like this:
public Boolean(boolean value) { this.value = value; } public Boolean(String s) { this(parseBoolean(s)); } public static boolean parseBoolean(String s) { return ((s != null) && s.equalsIgnoreCase("true")); }
The String “true” is case insensitive, but any leading or trailing whitespaces make the object FALSE. Also, please do not forget the parseBoolean()
method. We will talk about it later on. Let’s make a quick fact check.
public static void main(String[] args) throws Exception { Boolean boolTrueOne = new Boolean(true); Boolean boolTrueTwo = new Boolean(true); if (boolTrueOne == boolTrueTwo) { System.out.println("== comparison is true."); } if (boolTrueOne.equals(boolTrueTwo)) { System.out.println("equals() is true."); } }
The naive expectation is:
- there are two booleans
- they are true
- they are equal
- there are two Boolean objects
- their values are true
- they are different objects, so they are NOT equal
We can verify it with the following output.
It is exactly the same for the constructor Boolean(String)
. The case INSENSITIVE input to that constructor will create different objects. Moreover, they are also different than the objects created by the first constructor.
package blog.mertersualp.booleans; public class BooleanObjectCreations { public static void main(String[] args) throws Exception { Boolean boolTrueOne = new Boolean(true); Boolean boolTrueTwo = new Boolean(true); if (boolTrueOne == boolTrueTwo) { System.out.println("== comparison is true."); } if (boolTrueOne.equals(boolTrueTwo)) { System.out.println("equals() is true."); } Boolean boolTrueOneFromString = new Boolean(“TRue"); Boolean boolTrueTwoFromString = new Boolean("true"); if (boolTrueOneFromString == boolTrueTwoFromString) { System.out.println("== comparison is true."); } if (boolTrueOneFromString.equals(boolTrueTwoFromString)) { System.out.println("equals() is true."); } if (boolTrueOne == boolTrueTwoFromString) { System.out.println("== comparison is true."); } if (boolTrueOne.equals(boolTrueTwoFromString)) { System.out.println("equals() is true."); } } }
As now we expect, three comparisons made by the == operator all yield to false. Yet, a subtle modification, however, will instantly make all comparisons accepted.
package blog.mertersualp.booleans; public class BooleanObjectCreations { public static void main(String[] args) throws Exception { Boolean boolTrueOne = new Boolean(true); Boolean boolTrueTwo = new Boolean(true); if (boolTrueOne.booleanValue() == boolTrueTwo) { System.out.println("== comparison is true."); } if (boolTrueOne.equals(boolTrueTwo)) { System.out.println("equals() is true."); } Boolean boolTrueOneFromString = new Boolean("TRue"); Boolean boolTrueTwoFromString = new Boolean("true"); if (boolTrueOneFromString.booleanValue() == boolTrueTwoFromString) { System.out.println("== comparison is true."); } if (boolTrueOneFromString.equals(boolTrueTwoFromString)) { System.out.println("equals() is true."); } if (boolTrueOne.booleanValue() == boolTrueTwoFromString) { System.out.println("== comparison is true."); } if (boolTrueOne.equals(boolTrueTwoFromString)) { System.out.println("equals() is true."); } } }
What has changed? The only modification we did is supplying the primitive value of Boolean object that it represents to the left side of the == binary operator. This is done by the method written in bold, booleanValue()
. Here is the source code:
public boolean booleanValue() { return value; }
Having a primitive type on the left (or right) of the == binary operator changes the dynamics. Before, == operator simply wants to learn wether its operands are the same objects. It does not look at their values, but only there memory locations. When at least one operator becomes a primitive type, Java implicitly converts the non-primitive (i.e. reference) type into its corresponding primitive type. This is called unboxing. In Java terms, the reference type class is Wrapper class. The boolean primitive type is wrapped by Boolean class. The unboxing is replacing the object with its <primivite type>Value()
method. Remember that we explicitly called the booleanValue()
method for one operand. Java compiler implicitly makes the very same call for the other operand of ==. Therefore, all these operations reduces to primitive type checking.
The parseBoolean(String)
method may be preferred in place of Boolean(String)
constructor. It directly converts the input string into its corresponding primitive boolean value.
Reusing static
Booleans
There is another way to handle Boolean objects. At the very beginning of this post, I mentioned Boolean.TRUE
and Boolean.FALSE
static objects. The Boolean class contain those:
public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false);
These two objects are created when the Boolean class is initialised. From then on, we can always access them. We can also assign them to our declared objects instead of creating new Boolean objects each time. Now, let’s replace object creation with these static fields and remove the booleanValue()
methods.
package blog.mertersualp.booleans; public class BooleanObjectCreations { public static void main(String[] args) throws Exception { Boolean boolTrueOne = Boolean.valueOf(true); Boolean boolTrueTwo = Boolean.valueOf(true); if (boolTrueOne == boolTrueTwo) { System.out.println("== comparison is true."); } if (boolTrueOne.equals(boolTrueTwo)) { System.out.println("equals() is true."); } Boolean boolTrueOneFromString = Boolean.valueOf("TRue"); Boolean boolTrueTwoFromString = Boolean.valueOf("true"); if (boolTrueOneFromString == boolTrueTwoFromString) { System.out.println("== comparison is true."); } if (boolTrueOneFromString.equals(boolTrueTwoFromString)) { System.out.println("equals() is true."); } if (boolTrueOne == boolTrueTwoFromString) { System.out.println("== comparison is true."); } if (boolTrueOne.equals(boolTrueTwoFromString)) { System.out.println("equals() is true."); } } }
Here, we only use single one object, which is the static final Boolean TRUE
. SO, all comparisons, either object equality or value equality are all true.
Conclusion
What I like about the == operator while dealing with objects is not its comparison ability because it cannot do that properly. Its beauty is that it will teach us the underlying truth of the object’s origin. Is it a brand new object or does it refer to a previously created one? This answer to this question can be found by applying == to both operands.
Frankly, the == operator for objects is mostly used by mistake. Boolean objects inherently encourage this tendency, since they represent true and false. The techniques presented above may decrease the erroneous side effects.
Three take-aways from this study are:
- Using Boolean constructors should be avoided if no other way exists. Instead, applying the respective static
valueOf()
methods should be encouraged. - If somehow Boolean constructors are (or must be) employed and different Boolean objects are created, converting them to primitive boolean values by
booleanValue()
method is invaluable. - In the very end, we can reiterate an important but easily forgettable Java rule: Use
equals()
every time when comparing objects, not the == operator.