Learnitweb

Equals and clear method with a Protobuf object

In this tutorial, we will explore how a Protocol Buffers (Protobuf) generated Person object behaves in Java, especially in terms of:

  • Clearing fields
  • Object comparison
  • Immutability
  • Builder usage
  • Handling null values

1. Reusing the Existing Person Class

Assume you already generated a Person class from a .proto file and created a helper method to build a person object.

For example:

private Person createPerson() {
    return Person.newBuilder()
            .setName("Sam")
            .setAge(12)
            .build();
}

Moving creation logic into a method improves:

  • Reusability
  • Readability
  • Testability

Now we can easily create multiple Person instances.

2. Creating Two Person Objects

Let’s create two separate instances:

Person person1 = createPerson();
Person person2 = createPerson();

Even though both have identical values, they are still two different objects in memory.

3. Comparing Objects: equals() vs ==

Using equals()

log.info("Equals: {}", person1.equals(person2));

Protobuf overrides equals() to compare:

  • Field values
  • Not memory references

So if all field values are the same, equals() returns:

true

Because Protobuf treats messages like value objects, similar to Java records.

4. Using == (Reference Comparison)

log.info("Double Equals: {}", person1 == person2);

== checks whether both references point to the same object in memory. Since these are two separate objects:

false

5. Are Protobuf Objects Mutable?

Let’s try to modify:

person1.setName("Mike"); // Not possible

You will notice:

  • No setters exist
  • Only getters are available

Example:

person1.getName();
person1.getAge();

Once built:

  • You cannot change field values
  • The object becomes read-only

Why Immutability is Good

  • Thread-safe
  • Predictable behavior
  • No accidental state changes
  • Safe to share across components

6. Modifying an Existing Object Using toBuilder()

What if you want to change just one field? Instead of rebuilding from scratch:

Person person3 = person1.toBuilder()
        .setName("Mike")
        .build();

What Happens Here?

  • toBuilder() copies existing values
  • You modify only what you want
  • build() creates a new object

So:

  • person1 remains unchanged
  • person3 is a new instance

7. Comparing Modified Object

person1.equals(person3);

Since name differs:

false

And reference comparison:

person1 == person3;

Also:

false

8. Setting Fields to null

Suppose you try:

Person person4 = person1.toBuilder()
        .setName(null)
        .build();

You get:

NullPointerException

Protobuf does not allow null values.

Internally it enforces:

  • Non-null fields
  • Strict validation

This avoids many runtime bugs.

9. Correct Way: Clearing a Field

Instead of setting null, use:

Person person4 = person1.toBuilder()
        .clearName()
        .build();

What clearName() Does

  • Removes the field value
  • Treats it as “not set”
  • Not the same as null

10. Printing Objects

With Name Set

System.out.println(person3);

Output:

name: "Mike"
age: 12

With Name Cleared

System.out.println(person4);

Output:

age: 12

Notice:

  • Name is completely absent
  • Protobuf only prints set fields