为什么以下使用Cyclic Generics的代码无法编译?

时间:2015-07-29 16:52:50

标签: java generics java-8

以下是我的代码

class A<B2 extends B, A2 extends A<B2, A2>> {
    C<B2, A2> c;

    void test() {
        c.acceptParameterOfTypeA(this);
    }

}

class B {

}

class C<B2 extends B, A2 extends A<B2, A2>> {
    void acceptParameterOfTypeA(A2 a) {

    }
}

错误发生在c.acceptParameterOfTypeA(this);

错误是

  

类型C中的方法acceptParameterOfTypeA(A2)不是   适用于论点(A)

根据我的看法,方法acceptParameterOfTypeA需要一个类型为A的参数,而在给出错误的行中的this是A类型。

我做错了什么?如何解决这个问题?

如果它很重要,我使用Java8

4 个答案:

答案 0 :(得分:8)

我将再次重命名您的类,以便一切都更具可读性。所以,让我们:

public class First<T extends Second, U extends First<T, U>> {
    Third<T, U> c;

    void test() {
        c.acceptParameterOfTypeA(this);
    }

}

class Second {

}

public class Third<X extends Second, Y extends First<X, Y>> {
    void acceptParameterOfTypeA(Y a) {

    }
}

根据c成员(Third<T, U>)的定义,我们可以得出结论c将公开带有此签名的方法:

void acceptParameterOfTypeA(U a) { .. }

什么是UUFirst<T, U>的子类型。

但是,如果U在类型删除后可以用First替换,那么这将意味着First extends First<T, First>,这不是真的,因为U代表子类型First的{​​{1}},其参数化为SecondFirst的一些具体子类型。

要访问U,您可以应用所谓的 Get This 方法。

首先,由于您需要U,这是First的子类型,但无法从First获取,您可以引入abstract返回它的方法:

abstract class First<T extends Second, U extends First<T, U>> {
    Third<T, U> c;

    void test() {
        c.acceptParameterOfTypeA(getU());
    }

    abstract U getU();

}

然后,实现一个名为First的{​​{1}}样本子类,其Fourth扩展FirstT的一些具体类型,例如:

U

class Fourth extends First<Second, Fourth> { Fourth getU() { return this; } } 方法中,只需执行getU(),因为这将在超类中返回return this;的正确替代。

更多信息:

答案 1 :(得分:1)

简而言之,c.acceptParameterOfTypeA()接受A2this的类型为A<B2, A2>,但不知道是否A2。仅知道A2延伸A<B2, A2>

答案 2 :(得分:1)

根据kocko的答案,原始问题有相同的解决方案:

public class Main {
    abstract class A<A2 extends A<A2, B2>, B2 extends B<A2, B2>> {
        B2 b;

        void test() {
            b.testMethod(getThis()); //getThis() instead of this;
        }

        abstract A2 getThis();
    }

    class B<A2 extends A<A2, B2>, B2 extends B<A2, B2>> {
        void testMethod(A2 a) {

        }
    }

    public void execute() {

    }

    public static void main(String[] args) {
        Main main = new Main();
        main.execute();
    }
}

答案 3 :(得分:1)

我们可以通过删除不会导致问题的B部分来简化它 -

class A<T extends A<T>>
{
    void test(C<T> c)
    {
        c.acceptParameterOfTypeA(this);  // ERROR
    }
}

class C<T extends A<T>>
{
    void acceptParameterOfTypeA(T a) {}
}

this类型为A<T>;问题是A<T> <: T是否为假。

我们真正想要的是&#34;自我类型&#34;,以便this类型为T。我们在Java中没有这个。

通常我们将T extends A<T>用于&#34;自我类型&#34 ;;但在某些用例中存在缺陷和不足。

正如kocko所提到的那样,T getThis()就是一个补救办法。

你可以简单地做一个粗暴的演员(T)this,这显然是T的意图。

我首选的方法是简单地省略T的范围,并将其重命名为This以指示类型变量的用途。施放(This)this看起来显然是正确的。查看我的其他post。这种方法通常有效;但它不起作用,因为C需要This来绑定A<This>。更深层次的问题是AC相互依赖,可能会重新设计。