我有以下课程:
public abstract class A {
public String att;
public static abstract class Builder<T extends A> {
public T a;
public abstract T build();
public T.Builder setAtt(String a) {
this.a.att = a;
return this;
}
}
}
public class A1 extends A {
public static class Builder extends A.Builder<A1> {
public Builder() {
this.a = new A1();
}
public A1 build() {
return this.a;
}
}
}
public class A2 extends A {
public String subAtt;
public static class Builder extends A.Builder<A2> {
public Builder() {
this.a = new A2();
}
public A2 build() {
return this.a;
}
public Builder setSubAtt(String subAtt) {
a.subAtt = subAtt;
return this;
}
}
}
为什么A.setAtt
上的“无法从类型变量中选择”错误?
类型擦除不适用。 T
是A1
或A2
,但这在编译时是已知的。
我应该如何返回子类构建器呢?我的主要目标是能够在setter混合子类和超类之后进行setter。
答案 0 :(得分:5)
这不可行:
T.Builder
由于T
是类型变量,因此未绑定到任何特定类型,因此您不能指望编译器解析未知类型的嵌套类型。
T
可以是A1
或A2
,但这在编译时是已知的。
这个假设是错误的:假设您将代码作为JAR提供,而另一个开发人员使用它,引入他自己的A
子类。如果Java代码是在一个封闭的世界的假设下编译的,那么Maven将是一个无用的服务。
这个假设也是无关紧要的:你需要一个更复杂的类型系统来计算T.Builder
符合的一般类型。
答案 1 :(得分:3)
Marko解释说,你不能简单地使用T.Builder&#39;并期望编译器确定未知类型的嵌套类。
您可以做的是强制Builder
的子类标识自己:
public class A
{
public String att;
public static abstract class Builder<T extends A, U extends Builder<T, U>>
{
public T a;
public abstract T build();
public U setAtt(String a)
{
this.a.att = a;
return getBuilder();
}
public abstract U getBuilder();
}
}
public class A1 extends A
{
public static class Builder extends A.Builder<A1, A1.Builder>
{
public Builder()
{
this.a = new A1();
}
@Override
public A1 build()
{
return this.a;
}
@Override
public A1.Builder getBuilder()
{
return this;
}
}
}
这样的声明就像
A1 build = new A1.Builder().setAtt("x").build();
会起作用。