我有一个包含大量字段的JAVA类。它们基本上应该在构造函数阶段设置,永远不会改变。从语义上讲,这个类是不可变的。
public class A{
final int a;
final short b;
final double e;
final String f;
final String g;
//and more
}
问题是通常这些字段具有默认值,因此我不想总是给用户带来所有这些字段的构造函数。大多数时候,他们只需要设置几个。有几种方法可以解决这个问题:
这些都不是完全令人满意的。还有其他方法吗?谢谢。 一种方式
答案 0 :(得分:27)
我会使用参数类和fluent构建器API的组合来创建参数:
public class A {
private final int a;
private final short b;
private final double e;
private final String g;
public static class Aparam {
private int a = 1;
private short b = 2;
private double e = 3.141593;
private String g = "NONE";
public Aparam a(int a) {
this.a = a;
return this;
}
public Aparam b(short b) {
this.b = b;
return this;
}
public Aparam e(double e) {
this.e = e;
return this;
}
public Aparam g(String g) {
this.g = g;
return this;
}
public A build() {
return new A(this);
}
}
public static Aparam a(int a) {
return new Aparam().a(a);
}
public static Aparam b(short b) {
return new Aparam().b(b);
}
public static Aparam e(double e) {
return new Aparam().e(e);
}
public static Aparam g(String g) {
return new Aparam().g(g);
}
public static A build() {
return new Aparam().build();
}
private A(Aparam p) {
this.a = p.a;
this.b = p.b;
this.e = p.e;
this.g = p.g;
}
@Override public String toString() {
return "{a=" + a + ",b=" + b + ",e=" + e + ",g=" + g + "}";
}
}
然后像这样创建A的实例:
A a1 = A.build();
A a2 = A.a(7).e(17.5).build();
A a3 = A.b((short)42).e(2.218282).g("fluent").build();
A类是不可变的,参数是可选的,界面很流畅。
答案 1 :(得分:19)
你可以做两件事:
答案 2 :(得分:1)
这只是一个半严肃的建议,但我们可以将mikera's answer修改为类型安全。
说我们有:
public class A {
private final String foo;
private final int bar;
private final Date baz;
}
然后我们写:
public abstract class AProperty<T> {
public static final AProperty<String> FOO = new AProperty<String>(String.class) {};
public static final AProperty<Integer> BAR = new AProperty<Integer>(Integer.class) {};
public static final AProperty<Date> BAZ = new AProperty<Date>(Date.class) {};
public final Class<T> propertyClass;
private AProperty(Class<T> propertyClass) {
this.propertyClass = propertyClass;
}
}
和
public class APropertyMap {
private final Map<AProperty<?>, Object> properties = new HashMap<AProperty<?>, Object>();
public <T> void put(AProperty<T> property, T value) {
properties.put(property, value);
}
public <T> T get(AProperty<T> property) {
return property.propertyClass.cast(properties.get(property));
}
}
高级设计模式和/或模糊Java技巧的狂热爱好者会将此视为一种类型安全的异构容器。非常感激我也没有使用getGenericSuperclass()
。
然后,返回目标类:
public A(APropertyMap properties) {
foo = properties.get(AProperty.FOO);
bar = properties.get(AProperty.BAR);
baz = properties.get(AProperty.BAZ);
}
这都是这样使用的:
APropertyMap properties = new APropertyMap();
properties.put(AProperty.FOO, "skidoo");
properties.put(AProperty.BAR, 23);
A a = new A(properties);
对于lulz,我们甚至可以为地图提供流畅的界面:
public <T> APropertyMap with(AProperty<T> property, T value) {
put(property, value);
return this;
}
让来电者写信:
A a = new A(new APropertyMap()
.with(AProperty.FOO, "skidoo")
.with(AProperty.BAR, 23));
你可以对此做很多改进。 AProperty
中的类型可以更优雅地处理。 APropertyMap
可以有一个静态工厂而不是构造函数,如果你遇到那种东西,可以使用更流畅的代码。 APropertyMap
可以增加build
方法,该方法调用A
的构造函数,实际上将其转换为构建器。
您也可以使其中一些对象更通用。 AProperty
和APropertyMap
可以使用通用基类来执行功能位,并使用非常简单的A
特定子类。
如果您感觉特别是企业,并且您的域对象是JPA2实体,那么您可以使用元模型属性作为属性对象。这使得地图/构建器做了一些工作,但它仍然非常简单;我有一个通用的构建器工作在45行,每个实体的子类包含一个单行方法。
答案 3 :(得分:0)
一个有趣的选择是创建一个构造函数,该构造函数将Map<String,Object>
作为输入,其中包含用户想要指定的值。
构造函数可以使用映射中提供的值(如果存在),否则使用默认值。
修改强>
我认为随机选民已完全忽略了这一点 - 这并不总是最佳选择,但它是一项有用的技术,有几个优点:
答案 4 :(得分:0)
拥有许多字段可能表明一个课程做得太多。
也许您可以将类拆分为几个不可变类,并将这些类的实例传递给其他类的构造函数。这将限制构造函数的数量。