'java builder pattern and changing field

Im having difficulties to figure out how to change some field in a object created by builder pattern: for example this is the class

public class Pizza {
  private int size;
  private boolean cheese;
  private boolean pepperoni;
  private boolean bacon;

  public static class Builder {
    //required
    private final int size;

    //optional
    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean bacon = false;

    public Builder(int size) {
      this.size = size;
    }

    public Builder cheese(boolean value) {
      cheese = value;
      return this;
    }

    public Builder pepperoni(boolean value) {
      pepperoni = value;
      return this;
    }

    public Builder bacon(boolean value) {
      bacon = value;
      return this;
    }

    public Pizza build() {
      return new Pizza(this);
    }
  }

  private Pizza(Builder builder) {
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.bacon;
  }
}

and Pizza obect is created like this:

Pizza pizza = new Pizza.Builder(12)
                       .cheese(true)
                       .pepperoni(true)
                       .bacon(true)
                       .build();

now what I'm trying to find out is how to change in this object for example cheese field to false? I don't have getters and setters, I know I can use reflection but it makes code harder to read and understand. So is builder pattern useful to not final objects ?



Solution 1:[1]

The builder pattern is usually good to create immutable objects but it does not need to do so. If you'd like to be able to change values after build, you just need to add a setter for that.

Solution 2:[2]

Builder class should have methods to set the optional parameters and it should return the same Builder object after setting the optional attribute. As "cheese" is your optional parameter so you need a setter for it in your Builder class

public Builder cheese(Boolean cheese) {
            this.cheese = cheese;
            return this;
        }

Then you can use :

Pizza pizza1 = new Pizza.Builder(12)
                       .cheese(false)
                       .pepperoni(true)
                       .bacon(true)
                       .build();

Solution 3:[3]

The best way is to use Lombok's @Builder annotation. And you need to specify (toBuilder = true) to be able to convert the object back to the builder and modify the field.

@Builder(toBuilder = true)
public class Pizza {
 ...
}

So, you've created the Pizza
Pizza pizza = Pizza.builder()
                   .cheese(true)
                   .pepperoni(true)
                   .bacon(true)
                   .build();

And now you want to modify one field - super easy!

Pizza modifiedPizza = pizza.toBuilder()
                           .cheese(false)
                           .build();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Michael
Solution 2 Adya
Solution 3 Max