Tags: java
What's the difference between boolean and Boolean in Java?
At first, they look almost the same. Both can hold true or false.
The important difference is that boolean is a primitive, while Boolean is an object wrapper. That sounds small, but it matters a lot when you are working with JSON, DTOs, and PATCH requests.
boolean is a primitive type.
boolean pushToProduction = true;
A primitive boolean has only two possible values:
truefalseIt can never be null.
That also means Java has to give it a default value. For a field on a class, the default value is false.
class Patch {
private String name;
private boolean pushToProduction;
}
If a JSON payload does not contain pushToProduction:
{
"name": "Updated name"
}
Jackson still has to put some value into the Java field. Since the field is primitive, it becomes:
name = "Updated name";
pushToProduction = false;
The problem is that this loses information. We no longer know whether the client meant:
pushToProduction; leave it unchanged"pushToProduction to become false"Both cases become the same Java value: false.
Boolean is the object wrapper for boolean.
Boolean pushToProduction;
Because it is an object, it can represent three states:
Boolean.TRUEBoolean.FALSEnullThat third state is the useful part. null can mean "not provided".
class Patch {
private String name;
private Boolean pushToProduction;
}
For the same JSON payload:
{
"name": "Updated name"
}
Jackson can now deserialize it as:
name = "Updated name";
pushToProduction = null;
Now the application can tell the difference between an omitted value and an explicit false.
PATCH usually means partial update. The client sends only the fields it wants to change.
So this request:
{
"name": "Updated name"
}
should mean:
Change the name. Do not change anything else.
It should not mean:
Change the name, and also set every missing boolean to false.
This is where primitive boolean can introduce a subtle bug.
Imagine this DTO:
public record Patch(
String name,
boolean pushToProduction
) {}
If the request body is:
{
"name": "Updated name"
}
then pushToProduction becomes false, even though the client never sent it.
If the service forwards that DTO to another API, the outgoing JSON may accidentally become:
{
"name": "Updated name",
"pushToProduction": false
}
That is dangerous. A sparse update can accidentally clear an existing true value.
For partial updates, use Boolean when missing and explicit false have different meanings.
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public record Patch(
String name,
Boolean pushToProduction
) {}
Now the states stay separate:
| JSON input | Java value | Meaning | Serialized with NON_NULL
|
|---|---|---|---|
| field omitted | null |
do not change it | no |
"pushToProduction": true |
Boolean.TRUE |
set it to true | yes |
"pushToProduction": false |
Boolean.FALSE |
set it to false | yes |
With Boolean and NON_NULL, this input:
{
"name": "Updated name"
}
can stay sparse when forwarded:
{
"name": "Updated name"
}
That preserves the meaning of the PATCH request.
Once a field is Boolean, avoid this:
if (patch.pushToProduction()) {
publishToBlockchain();
}
If pushToProduction is null, unboxing it to boolean can throw a NullPointerException.
Use this instead:
if (Boolean.TRUE.equals(patch.pushToProduction())) {
publishToBlockchain();
}
This condition is true only when the value is explicitly Boolean.TRUE.
For merge logic, check for null when you want to know whether the client sent the field:
if (patch.pushToProduction() != null) {
patch.setpushToProduction(patch.pushToProduction());
}
This means:
null: do not updateBoolean.TRUE: update to trueBoolean.FALSE: update to falseUse primitive boolean when the value is always required and there is a real default.
Use wrapper Boolean when absence has meaning.
For JSON PATCH DTOs, absence usually has meaning. If the client does not send a boolean field, the safest assumption is often "do not change it", not "set it to false".