为什么"不能从类型变量中选择"

Gus*_*ust 7 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


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;
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)


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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么我得到" 无法从类型变量中选择 "错误A.setAtt?

类型擦除不应该​​适用.T是或者是A1,A2但这在编译时是已知的.

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

Mar*_*nik 5

这不可行:

T.Builder
Run Code Online (Sandbox Code Playgroud)

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

T是或者是A1,A2但这在编译时是已知的.

这个假设是错误的:假设您将代码作为JAR提供,而另一个开发人员使用它,引入他自己的子类A.如果Java代码是在一个封闭的世界的假设下编译的,那么Maven将是一个无用的服务.

这个假设也无关紧要:你需要一个更复杂的类型系统来计算出一般类型T.Builder符合的类型.


flo*_*flo 3

正如 Marko 所解释的,您不能简单地使用“T.Builder”并期望编译器确定未知类型的嵌套类。

你可以做的是强制 的子类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();        


    }        
}
Run Code Online (Sandbox Code Playgroud)


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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这样一个声明就像

A1 build = new A1.Builder().setAtt("x").build();   
Run Code Online (Sandbox Code Playgroud)

会工作。