web-dev-qa-db-ja.com

クラスジェネリックの型の不一致

コンパイルされない次のコードがあります。コンパイルする方法はありますが、コンパイルされない理由を理解したいと思います。最後に投稿するエラーメッセージが表示される理由を誰かに教えてもらえますか?

public class Test {
    public static void main(String args[]) {
        Test t = new Test();
        t.testT(null);
    }

    public <T extends Test> void testT(Class<T> type) {
        Class<T> testType = type == null ? Test.class : type; //Error here
        System.out.println(testType);
    }
}

Type mismatch: cannot convert from Class<capture#1-of ? extends Test> to Class<T>

Test.classClass<T>にキャストすることにより、これはUnchecked cast警告とともにコンパイルされ、完全に実行されます。

20
Henry B

その理由は、Test.classのタイプがClass <Test>であるためです。 Class <Test>型の参照は、同じものではないため、Class <T>型の変数に割り当てることはできません。ただし、これは機能します。

_Class<? extends Test> testType = type == null ? Test.class : type;
_

ワイルドカードを使用すると、Class <T>とClass <Test>の両方の参照をtestTypeに割り当てることができます。

Javaジェネリックスの動作 Angelika Langer JavaジェネリックスFAQ )に関する情報はたくさんあります。 Numberクラス階層JavaのコアAPIを使用する情報の一部。

次の方法を検討してください。

_public <T extends Number> void testNumber(final Class<T> type)
_

これは、次のステートメントを正常にコンパイルできるようにするためです。

_testNumber(Integer.class);
testNumber(Number.class);
_

ただし、以下はコンパイルされません。

_testNumber(String.class);
_

ここで、次のステートメントについて考えてみましょう。

_Class<Number> numberClass = Number.class;
Class<Integer> integerClass = numberClass;
_

2行目はコンパイルに失敗し、このエラー_Type mismatch: cannot convert from Class<Number> to Class<Integer>_を生成します。しかし、IntegerNumberを拡張しているのに、なぜ失敗するのでしょうか。次の2つのステートメントを見て、理由を確認してください。

_Number anumber = new Long(0);
Integer another = anumber;
_

2行目がここでコンパイルされない理由は簡単にわかります。 Numberインスタンスが互換性のあるタイプであることを保証する方法がないため、IntegerのインスタンスをタイプNumberの変数に割り当てることはできません。この例では、Numberは実際にはLongであり、これをIntegerに割り当てることはできません。実際、エラーはタイプの不一致でもあります:_Type mismatch: cannot convert from Number to Integer_。

互換性の保証がないため、インスタンスのタイプのサブクラスである変数にインスタンスを割り当てることはできません。

ジェネリックスも同様に動作します。汎用メソッドシグネチャでは、Tは、メソッドがコンパイラに何を許可するかを示す単なるプレースホルダーです。コンパイラがtestNumber(Integer.class)を検出すると、基本的にTIntegerに置き換えます。

ワイルドカードは、以下がコンパイルされるため、柔軟性を追加します。

_Class<? extends Number> wildcard = numberClass;
_

_Class<? extends Number>_は、NumberまたはNumberのサブクラスである任意の型を示すため、これは完全に合法であり、多くの状況で役立つ可能性があります。

23
laz

テストを拡張するとします。

public class SubTest extends Test {
  public static void main(String args[]) {
    Test t = new Test();
    t.testT(new SubTest());
  }
}

さて、testTを呼び出したとき、型パラメーター<T>SubTestです。これは、変数testTypeClass<SubTest>であることを意味します。 Test.classはタイプClass<Test>であり、タイプClass<SubTest>の変数に割り当てることはできません。

変数testTypeClass<? extends Test>として宣言するのが正しい解決策です。 Class<T>にキャストすると、実際の問題が隠されます。

4
erickson

条件文を削除すると、エラーは少し良くなります...

public class Test {
    public static void main(String args[]) {
        Test t = new Test();
        t.testT(null);
    }

    public <T extends Test> void testT(Class<T> type) {
    Class<T> testClass = Test.class;
        System.out.println(testClass);
    }
}


Test.Java:10: incompatible types
found   : Java.lang.Class<Test>
required: Java.lang.Class<T>
        Class<T> testClass = Test.class;
1
Ken