The following class:
class Pizza {
Ingredients ingredients;
Price price;
public setIngredients(Ingredients ing) {
if (ingredients != null) {
throw new IllegalStateException();
}
ingredients = ing;
return this;
}
public setPrice(Price p) {
if (price != null) {
throw new IllegalStateException();
}
price = p;
return this;
}
}
could be used in a builder pattern, and after it has been built, it's effectively immutable, because each property can be set only once. That is:
Pizza pizza = new Pizza().setIngredients(something).setPrice(somethingelse);
However, Pizza
is not thread safe: there are no guarantees that thread B sees the ingredients that were set into it by the thread A. There are some ways to fix it:
- Make members
final
. But then you can't use a builder pattern. - Synchronize access to members. But this seems like waste, because they're written only once ever.
- Make them
volatile
. Feels waste, like synchronization. - Use
AtomicReference
. - Etc.?
My question is, what is the best way to tell the JVM that a class member won't change after some method has been called? Should I just synchronize access to it, and trust that the JVM will optimize the lock away? It just feels waste, because I know that the member should behave like it's final
after it's set. Aren't there any better solutions?
Builder pattern usually means that builder is a separate object. In this case you can make fields of the object being built
final
, and initialize them in constructor called by the builder object:Alternatively, you can ensure safe publication of the
Pizza
object. Note that safe publication idioms are applied to the field that contains a reference to the object being published, not to the fields of that object itself. For example, ifpizza
is a field of some object, you can make itvolatile
or synchronize access to it - it would ensure safe publication ofPizza
object assigned to that field.