I have the following classes:
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;
}
}
}
Why do I get "cannot select from a type variable" error on the A.setAtt
?
Type erasure shouldn't apply. T
is either A1
or A2
but this is known at compile-time.
How should I return the subclass builder then? My main objective is to be able to do setter after setter mixing subclass and superclass.
This cannot possibly work:
T.Builder
Since T
is a type variable and therefore not bound to any specific type, you cannot expect the compiler to resolve a nested type of an unknown type.
T
is eitherA1
orA2
but this is known at compile-time.
This assumption is wrong: imagine you offer your code as a JAR and another developer uses it, introducing his own subclass of A
. If Java code was compiled under the assumption of a closed world, Maven would be a most useless service.
The assumption is also irrelevant: you would need a quite more sophisticated type system to work out what general type T.Builder
conforms to.
As Marko explained, you cannot simply use 'T.Builder' and expect the compiler to determine the nested class of an unknown type.
What you can do is to force a subclass of Builder
to identify itself:
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;
}
}
}
This way a Statement like
A1 build = new A1.Builder().setAtt("x").build();
would work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With