将可变对象转换为不可变对象

时间:2016-02-04 23:39:06

标签: java immutability

我想使bean不可变,传统的方法是创建所有字段final并在构造函数中设置它们的值。

在我看来,这很有效,除非有许多字段需要通过一些复杂的逻辑相互依赖地计算,或者当多个服务需要参与设置值时。

工厂是另一种方法,但我无法想出一个避免过多代码的整洁模型。

我想要做的是创建一个bean的可变实例,一旦它被完全填充,我想“烘焙”它,即使它变得不可变。

有没有办法在运行时将实例更改为从non-final更改为final而不进行子类化等。

我很确定它不能仅使用标准的java /反射来完成,但我怀疑它可能使用一些字节码改变,例如javassist等。

一个漂亮整洁的工厂模式也可能会得到这个标记......

1 个答案:

答案 0 :(得分:2)

Joshua Bloch在他的书“ Effective Java,Second Edition ”的第二章“创建和销毁Java对象”中解决了这个问题。 You can view an example taken from his book here

// Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
            { calories = val;      return this; }
        public Builder fat(int val)
            { fat = val;           return this; }
        public Builder carbohydrate(int val)
            { carbohydrate = val;  return this; }
        public Builder sodium(int val)
            { sodium = val;        return this; }

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

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

使用此对象的示例是:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();

从上面的示例中可以看出,您可以在将可变对象构建为不可变对象之前先创建它。还可以调整对象的构建器并重新使用它来生成多个不可变对象。例如:

NutritionFacts.Builder food = new NutritionFacts.Builder(1, 1);
NutritionFacts salad = food.calories(100).build();
NutritionFacts bigMac = food.calories(1000).build();