【发布时间】:2016-08-15 03:49:23
【问题描述】:
我知道有类似的问题。不过,我还没有看到我的问题的答案。
我会用一些简化的代码来展示我想要的东西。假设我有一个复杂的对象,它的一些值是通用的:
public static class SomeObject<T, S> {
public int number;
public T singleGeneric;
public List<S> listGeneric;
public SomeObject(int number, T singleGeneric, List<S> listGeneric) {
this.number = number;
this.singleGeneric = singleGeneric;
this.listGeneric = listGeneric;
}
}
我想用流畅的 Builder 语法来构建它。不过,我想让它变得优雅。我希望它像那样工作:
SomeObject<String, Integer> works = new Builder() // not generic yet!
.withNumber(4)
// and only here we get "lifted";
// since now it's set on the Integer type for the list
.withList(new ArrayList<Integer>())
// and the decision to go with String type for the single value
// is made here:
.withTyped("something")
// we've gathered all the type info along the way
.create();
没有不安全的强制转换警告,也不需要预先指定泛型类型(在顶部,构建 Builder 的地方)。
相反,我们让类型信息显式地流入到链的更下游 - 以及 withList 和 withTyped 调用。
现在,实现它的最优雅的方式是什么?
我知道最常见的技巧,例如使用recursive generics,但我玩弄了一段时间,不知道它如何应用于这个用例。
下面是一个普通的冗长解决方案,它在满足所有要求的意义上工作,但代价是非常冗长 - 它引入了四个构建器(在继承方面无关),代表T 和 @987654327 的四种可能组合@ 类型是否被定义。
它确实有效,但这不是一个值得骄傲的版本,如果我们期望更多的通用参数而不仅仅是两个,那么它就无法维护。
public static class Builder {
private int number;
public Builder withNumber(int number) {
this.number = number;
return this;
}
public <T> TypedBuilder<T> withTyped(T t) {
return new TypedBuilder<T>()
.withNumber(this.number)
.withTyped(t);
}
public <S> TypedListBuilder<S> withList(List<S> list) {
return new TypedListBuilder<S>()
.withNumber(number)
.withList(list);
}
}
public static class TypedListBuilder<S> {
private int number;
private List<S> list;
public TypedListBuilder<S> withList(List<S> list) {
this.list = list;
return this;
}
public <T> TypedBothBuilder<T, S> withTyped(T t) {
return new TypedBothBuilder<T, S>()
.withList(list)
.withNumber(number)
.withTyped(t);
}
public TypedListBuilder<S> withNumber(int number) {
this.number = number;
return this;
}
}
public static class TypedBothBuilder<T, S> {
private int number;
private List<S> list;
private T typed;
public TypedBothBuilder<T, S> withList(List<S> list) {
this.list = list;
return this;
}
public TypedBothBuilder<T, S> withTyped(T t) {
this.typed = t;
return this;
}
public TypedBothBuilder<T, S> withNumber(int number) {
this.number = number;
return this;
}
public SomeObject<T, S> create() {
return new SomeObject<>(number, typed, list);
}
}
public static class TypedBuilder<T> {
private int number;
private T typed;
private Builder builder = new Builder();
public TypedBuilder<T> withNumber(int value) {
this.number = value;
return this;
}
public TypedBuilder<T> withTyped(T t) {
typed = t;
return this;
}
public <S> TypedBothBuilder<T, S> withList(List<S> list) {
return new TypedBothBuilder<T, S>()
.withNumber(number)
.withTyped(typed)
.withList(list);
}
}
我可以应用更聪明的技术吗?
【问题讨论】:
-
"unmaintainable if we expect more generic parameters than just two" 如果你想保留任意顺序(在你的例子中,你可以同时做
withTyped(...).withList(...)和withList(...).withTyped(...)) 那么问题就变得非常困难,因为你最终会得到类似n!类的东西,其中n是类型参数的数量。如果您采用更传统的 step-builder 方法,那么它会更简单一些。 -
@Radiodef 我认为 2^n 个类:在任何时候,每个泛型类型都可以处于以下两种状态之一:已定义或尚未定义。但是,是的,这是“手动”实现的一个严重缺点。这就是为什么我想知道是否存在更好的解决方案;也许以某种巧妙的方式利用通用约束。
标签: java generics builder fluent