【问题标题】:Why Does the following code with Cyclic Generics not compile?为什么以下带有循环泛型的代码无法编译?
【发布时间】:2015-07-29 16:52:50
【问题描述】:

以下是我的代码

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

【问题讨论】:

  • class B&lt;A2 extends A&gt; { 那里,A 参数对我来说看起来很原始。
  • @EpicPandaForce 是的,我想我可能拿错了例子。会尝试看看我是否可以改进。谢谢!
  • @EpicPandaForce 谢谢,我用我面临的实际情况更新了这个问题。
  • 您可以将问题简化为:class A&lt;A2 extends A&lt;A2&gt;&gt; { void test() { A2 x = this; } }

标签: java generics java-8


【解决方案1】:

我将再次重命名您的类,以便所有内容更具可读性。所以,让我们有:

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&lt;T, U&gt;)的定义中,我们可以得出结论c 将公开具有此签名的方法:

void acceptParameterOfTypeA(U a) { .. }

U 是什么? UFirst&lt;T, U&gt; 的子类型。

但是如果U在类型擦除后可以替换为First,这将意味着First extends First&lt;T, First&gt;,这是不正确的,因为U代表First的子类型,它是参数化的有一些具体的子类型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 的示例子类,称为Fourth,它使用TU 的一些具体类型扩展First,例如:

class Fourth extends First<Second, Fourth> {
    Fourth getU() {
        return this;
    }
}

getU() 方法中,只需执行return this;,因为这将返回超类中U 的正确替换。

更多信息:

【讨论】:

    【解决方案2】:

    简单地说,c.acceptParameterOfTypeA() 接受 A2this 的类型为 A&lt;B2, A2&gt;,未知其扩展 A2。只知道A2 扩展了A&lt;B2, A2&gt;

    【讨论】:

      【解决方案3】:

      根据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();
          }
      }
      

      【讨论】:

        【解决方案4】:

        我们可以通过删除不会导致问题的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&lt;T&gt;;问题是A&lt;T&gt; &lt;: T是否为假。

        这里我们真正想要的是“自我类型”,所以this 类型是T。我们在 Java 中没有。

        通常我们使用T extends A&lt;T&gt; 表示“自我类型”;但在某些用例中存在缺陷和不足。

        正如 kocko 所提到的,一种解决方法是 T getThis()

        你可以简单地对(T)this 进行暴力转换,这显然是T 的意图正确的。


        我的首选方法是简单地省略T 的边界,并将其重命名为This 以指示类型变量的用途。铸造(This)this 看起来显然是正确的。见我的另一个post。这种方法通常有效;但它在这里不起作用,因为C 需要This 才能绑定A&lt;This&gt;。更深层次的问题是AC 相互依赖,可能需要重新设计。

        【讨论】:

          猜你喜欢
          • 2020-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多