سؤال

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:

Pizza pizza = 
    new PizzaBuilder()
        .setIngredients(something)
        .setPrice(somethingelse)
        .build(); 

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, if pizza is a field of some object, you can make it volatile or synchronize access to it - it would ensure safe publication of Pizza object assigned to that field.

نصائح أخرى

If the member values are never meant to change, the a better pattern would be to have a Pizza constructor that takes as its parameters Ingredients and Price, and have no setter methods on the object at all. Really it is not useful to have a setSomething() method that throws an exception after the first time it is called.

Consider how the String class works. Once you have instantiated a String with some text, the text value cannot be changed. The only way to get a String with a different value is to construct a new one. It seems like that is what you want here.

Using this pattern also avoids the synchronization issue.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top