在新建对象时,若需要对大量可选参数进行赋值,最常见的做法是JavaBeans模式,即调用一个无参构造方法创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。代码示例如下:
Copypublic class Complex { private int size; private int color; private int range = 0; private int num = 0; public Complex(int size, int color) { this.size = size; this.color = color; } public void setRange(int range) { this.range = range; } public void setNum(int num) { this.num = num; } }
这种模式需要的代码语句繁琐,而且这种做法阻止了把类做成不可变的可能。更加简洁的一种方式是通过多个构造方法去新建对象。比如下面的代码:
Copypublic class Complex { private int size; private int color; private int range = 0; private int num = 0; public Complex(int size, int color) { this.size = size; this.color = color; } public Complex(int size, int color, int range) { this.size = size; this.color = color; this.range = range; } // public Complex(int size, int color, int num) { // this.size = size; // this.color = color; // this.num = num; // } public Complex(int size, int color, int range, int num) { this.size = size; this.color = color; this.range = range; this.num = num; } }
当参数越来越多时,这种方式就会使类的结构变得臃肿,难以维护,在使用时也需要去逐个理解每种构造方法的参数意义。并且在上述示例中,也是无法打开被注释掉的构造方法。
有没有一种更好的创建大量可选参数对象的方式?答案是Builder模式。先来看下面的示例代码:
Copypublic class NutritionFacts { private final int size; private final int color; private final int num; private final int range; public static class Builder { private int size; private int color; private int range = 0; private int num = 0; public Builder(int size, int color) { this.size = size; this.color = color; } public Builder num(int num) { this.num = num; return this; } public Builder range(int range) { this.range = range; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { this.color = builder.color; this.num = builder.num; this.size = builder.size; this.range = builder.range; } public static void main(String[] args) { NutritionFacts build = new NutritionFacts.Builder(2, 1) .num(3).range(4).build(); } }
通过观察示例代码,不难发现。这种模式的本质是不直接生成想要的对象,而是通过链式编程构建一个参数完备的构造器对象,最终通过调用构造器对象的build()方法来生成不可变的目标对象。
显而易见,链式编程的秘诀在于从实例化对象开始,每次调用成员方法都会返回自身对象。
上述示例略微繁琐,若是追求简便,似乎有更好的另一种写法,示例代码如下:
Copypublic class NutritionFacts { private int size; private int color; private int num; private int range; public static class Builder { private NutritionFacts nutritionFacts = new NutritionFacts(); public Builder(int size, int color) { this.nutritionFacts.size = size; this.nutritionFacts.color = color; } public Builder num(int num) { this.nutritionFacts.num = num; return this; } public Builder range(int range) { this.nutritionFacts.range = range; return this; } public NutritionFacts build() { return nutritionFacts; } } public static void main(String[] args) { NutritionFacts facts = new NutritionFacts.Builder(2, 1) .num(3).range(4).build(); } }
写到这里,笔者打算扩展下内部类与静态内部类的相关知识。
内部类与静态内部类
观察下面的静态内部类的声明,static关键字位于class关键字之前(顺序颠倒会报错),说明static的规则优先于class的规则。理解了这一点,就不难理解如何使用静态内部类。
Copypublic class InnerClass { public class Inner{ public Inner(){ //.. } } public static class StaticInner{ static int i = 10; public StaticInner(){ //.. } } }
对于内部类的实例化:
因为内部类属于外部类的非静态成员,所以首先需要实例化外部类,其次因为内部类也属于类,使用之前故也需要实例化。
Copy public static void main(String[] args) { new InnerClass().new Inner(); }
对于静态内部类的实例化:
因为static关键字必须声明在class之前,所以可知static的规则优先于class的规则。在处理类的静态成员时,优先使用“类名.静态成员”的形式,其次,再考虑实例化或其它变量引用。
Copy public static void main(String[] args) { new InnerClass.StaticInner(); int i = InnerClass.StaticInner.i; }