《Effective Java》(第2版)中第二条中提到:遇到多个构造器参数时要考虑用构建器。在复习static关键字和内部类时回头看了一下,这才明白了为什么要用静态内部类来做处理,这里记录一下。
先看再看一下《Effective Java》书中的例子,例子中是用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个是必需的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等。如果用一般的处理方式,直接把这些参数在构造器中初始化的话,有两个缺点:
1、没法体现营养成分的必需和非必需属性
2、参数太多,这样在实例化传参数的时候其实比较难区分的。因为他们几乎都是int型变量,而且即使传递错了也不会有任何提醒。
基于上面的问题,书中转而使用Builder模式,当然,Builder模式能解决上面的问题,那么也间接说明了Builder模式的优点。(1、区分必传和选传参数2、比较明确的表示出参数的含义)
这里直接把书中代码搬过来:
1 public class NutritionFacts { 2 private final int servingSize;//份容量 3 private final int servings;//份 4 private final int calories;//卡路里 5 private final int fat;//脂肪 6 private final int sodium;//钠 7 private final int carbohydrate;//糖类 8 public static class Builder{ 9 private final int servingSize;//份容量 10 private final int servings;//份 11 //必要参数 12 public Builder(int servingSize,int servings){ 13 this.servingSize=servingSize; 14 this.servings=servings; 15 } 16 private int calories = 0; 17 private int fat = 0; 18 private int sodium = 0; 19 private int carbohydrate = 0; 20 public Builder calories(int val){ 21 calories = val; 22 return this; 23 } 24 public Builder fat(int val){ 25 fat = val; 26 return this; 27 } 28 public Builder sodium(int val){ 29 sodium = val; 30 return this; 31 } 32 public Builder carbohydrate(int val){ 33 carbohydrate = val; 34 return this; 35 } 36 public NutritionFacts build(){ 37 return new NutritionFacts(this); 38 } 39 } 40 private NutritionFacts(Builder builder){ 41 servingSize = builder.servingSize; 42 servings = builder.servings; 43 calories = builder.calories; 44 fat = builder.fat; 45 sodium = builder.sodium; 46 carbohydrate = builder.carbohydrate; 47 } 48 }
使用时比较简单,而且比较清晰:
1 NutritionFacts cocoCola = new NutritionFacts.Builder(20,15).calories(11).carbohydrate(12).build();
注意点:
1、对于NutritionFacts来说,包含了三部分,final成员变量,私有构造器、静态内部类。final成员变量保证了初始化安全性,即在构造器执行完成之前必须显示对成员变量初始化。这里是在构造函数中传入Builder来对其初始化。
2、构造器为什么要声明为私有的?保证了外部创建实例时只能通过静态内部类的build()方法来实现。
3、为什么是静态内部类?因为构造器是私有的,导致外部只能通过内部类的build()方法来实现。而非静态内部类对象的创建又依赖于外部类对象,即必须有外部类对象来创建(外部类对象.new InnerClassName()),这样就陷入了死循环。而静态内部类不需要依赖于外部类对象,只需要通过 “new OutClassName.InnerClassName()”就可以完成实例化。
4、内部类Builder通过final关键字来区分必需参数和非必需参数。通过builder()方法完成外部类实例化。这里利用内部类可以访问外部私有元素的特性。
5、总的来看,就是外部类通过静态内部类完成了自己成员变量的初始化。
参考书籍:
《Effective Java》