为什么在这里无法推断泛型类型?

4

假设我有以下类:

interface MyS<T extends MyT<S, T>, S extends MyS<T, S>> {
}
interface MyT<S extends MyS<T, S>, T extends MyT<S, T>> {
}
public class MySImpl implements MyS<MyTImpl, MySImpl> {
}
public class MyTImpl implements MyT<MySImpl, MyTImpl> {
}

我可以构建以下测试用例,它可以成功编译和运行,而且没有任何警告:

public class STTest {

  @Test
  public void test() throws Exception {
    createInstance(MyTImpl.class);
  }

  public static<T extends MyT<S, T>, S extends MyS<T, S>> void createInstance(
      final Class<? extends MyT<S, T>> beanClass) throws Exception {

    final MyT<S, T> bean = beanClass.newInstance();
  }
}

好的,没问题。但是我希望这样做能够产生相同的效果:

public class STTest {

  @Test
  public void test() throws Exception {
    createInstance(MyTImpl.class);
  }

  public static<T extends MyT<S, T>, S extends MyS<T, S>> void createInstance(
      final Class<T> beanClass) throws Exception {

    final T bean = beanClass.newInstance();
  }
}

然而,这是一个编译错误:

S的推断类型无效;推断类型不符合声明的边界 inferred: MySImpl bound(s): MyS

为什么会出现这种情况?

更新:

我注意到这种行为与编译器有关。我使用了OpenJDK 1.6编译器(javac 1.6.0_27)进行编译,但它失败了。然而:

  • OpenJDK 1.7编译器(javac 1.7.0_21)和
  • Oracle 1.6编译器(javac 1.6.0_37)

两者在第二个示例中都能正常工作。

然而:这是OpenJDK 1.6编译器的一个错误还是Java语言规范中的歧义?


3
我可以运行你的代码,且没有出现编译错误... - darijan
一样的问题。你的接口声明缺少括号,只有编译错误。 - Davey Chu
我要补充一下,它还可以使用Eclipse编译器编译Java 7。 - Paul Bellora
@darijan:我尝试切换编译器,……它可以工作了。请查看我的问题更新。@davechu:已修复。 - yankee
这样循环引用的通用接口有什么用处呢?它似乎比它所值得的更加令人困惑... - JAB
1个回答

3
在第二个示例中,参数类型没有以任何方式提及S。编译器告诉你它因此无法推断出来。
更具体地说,在第一个示例中,你要求一个Class<? extends MyT<S, T>>。在test()中,你给它一个Class<MyTImpl>。当编译器检查限制时,它将MyT<S, T>MyTImpl匹配,并发现MyTImpl实现了MyT<MySImpl,MyTImpl>,所以它可以通过简单地将这两个东西放在一起来推断出SMySImplTMyTImpl
然后,它检查ST的约束条件,对于MySImplMyTImpl成功。
在第二个示例中,你要求一个Class<T>。编译器看到Class<MyTImpl>,推断出MyTImplT,然后就完成了。由于未能推断出S,它报错。
T进行约束检查,这可以提供S的信息,但是没有发生。

1
是的,这种情况确实不会发生。但为什么呢?与此同时,我发现这种行为取决于编译器。因此,很明显它是可能的。 - yankee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接