8

このコードがコンパイルされないのはなぜですか?

public class x
{
    private void test()
    {
        handle(new ThingA());
        handle(new ModifiedThingA());
    }

    private <T extends BaseThing<T>, X extends T> java.util.List<T> handle(X object)
    {
        return object.getList();
    }

    private static class BaseThing<T extends BaseThing<T>>
    {
        public java.util.List<T> getList()
        {
            return null;
        }
    }

    private static class ThingA
        extends BaseThing<ThingA>
    {
    }

    private static class ModifiedThingA
        extends ThingA
    {
    }
}

Java 6は、このエラーをhandle(new ModifiedThingA());次のように示します。

x.java:6: <T,X>handle(X) in x cannot be applied to (x.ModifiedThingA)
            handle(new ModifiedThingA());
            ^

Java 7は好きではありませんhandle(new ThingA());、これはJava7の出力です。

x.java:5: error: invalid inferred types for T; inferred type does not conform to declared bound(s)
            handle(new ThingA());
                  ^
    inferred: ThingA
    bound(s): CAP#1
  where T,X are type-variables:
    T extends BaseThing<T> declared in method <T,X>handle(X)
    X extends T declared in method <T,X>handle(X)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends BaseThing<CAP#1> from capture of ?
x.java:6: error: invalid inferred types for T; inferred type does not conform to declared bound(s)
            handle(new ModifiedThingA());
                  ^
    inferred: ModifiedThingA
    bound(s): CAP#1
  where T,X are type-variables:
    T extends BaseThing<T> declared in method <T,X>handle(X)
    X extends T declared in method <T,X>handle(X)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends BaseThing<CAP#1> from capture of ?
2 errors

それが実際にあるのに、それをjavac間違えているように私には思えます。これは私のバグですか、それとものバグですか?ModifiedThingABaseThing<ModifiedThingA>BaseThing<ThingA>javac

4

3 に答える 3

1

javacの動作は正しいようです。理論的には、1つの型変数でT十分です。Xただし、型推論に役立つ2番目の型変数、を導入しました。の引数Xが最初に推測され、次に呼び出しコンテキストに基づいて、の引数Tが推測されます。

List<ThingA> a = handle(new ThingA());
List<ThingA> b = handle(new ModifiedThingA());

ただし、呼び出しコンテキストでは、戻り型に制限はありません。したがって、コンパイラは、null型を下限として型変数()を導入することを余儀なくされます。の引数は。として推測されます。その下限を考えると、はおそらくのサブタイプではありません。CAP#1Tglb(BaseThing<CAP#1>) = BaseThing<CAP#1>XT

これには2つまたは3つの方法があります。

  1. 手動で推測します(めったに必要でない場合は大丈夫です)
  2. リターンタイプがvoidの「オーバーロード」を提供します(別の名前が必要です、urgh)
  3. 返されたリストが不変または防御コピーである場合は、type引数Tをそのbounds引数から切断できます

私はオプション3を好みます:

private <T extends BaseThing<T>> List<T> handle(BaseThing<? extends T> object) {
    return new ArrayList<T>(object.getList());
    // or (using guava's ImmutableList)
    return ImmutableList.copyOf(object.getList());
}

幸せなジェネリック。

于 2012-08-21T18:48:28.323 に答える
1

コードはjavac1.8.0_45およびEclipse4.1.1で正常にコンパイルされます。

おそらく、ラムダ式を適切にコンパイルするためにJava 8の型推論アルゴリズムに加えられた変更も、問題を解決しました。

于 2015-11-04T12:44:02.767 に答える
0

javac 1.7.0_09および1.7.0_11にバグがあり、この問題が発生しているようです。

于 2013-01-21T16:48:39.820 に答える