为什么"无法从类型变量中选择"

时间:2015-02-10 14:22:38

标签: java generics builder

我有以下课程:

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上的“无法从类型变量中选择”错误?

类型擦除不适用。 TA1A2,但这在编译时是已知的。

我应该如何返回子类构建器呢?我的主要目标是能够在setter混合子类和超类之后进行setter。

2 个答案:

答案 0 :(得分:5)

这不可行:

T.Builder

由于T是类型变量,因此未绑定到任何特定类型,因此您不能指望编译器解析未知类型的嵌套类型。

  

T可以是A1A2,但这在编译时是已知的。

这个假设是错误的:假设您将代码作为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();   

会起作用。