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 の推論された型が無効です。推論された型は、推論された宣言された境界に準拠していません: MySImpl 境界: 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)

2 番目の例では、どちらも正常に動作します。

しかし、これは OpenJDK 1.6 コンパイラのバグですか、それとも Java 言語仕様のあいまいさですか?

4

1 に答える 1

3

2 番目の例では、引数の型に S がまったく言及されていません。コンパイラは、したがってそれを推論できないと言っています。

詳しく説明します。最初の例では、Class<? extends MyT<S, T>>. ではtest()、 を与えますClass<MyTImpl>。コンパイラが境界をチェックするとき、それはMyT<S, T>に対して一致し、それがimplementsであることMyTImplを検出するため、単にこれら 2 つを並べて配置するだけで forとforを推測できます。MyTImplMyT<MySImpl, MyTImpl>MySImplSMyTImplT

S次に、およびTの制約をチェックし、MySImpl成功MyTImplします。

2 番目の例では、Class<T>. コンパイラは を見てClass<MyTImpl>、 を推測MyTImplT、完了します。の推測Sに失敗すると、エラーが発生します。

Tに関する情報を提供できる の制約チェックSは行われません。

于 2013-06-13T17:11:11.210 に答える