1

これを言葉で試みるのではなく、例を挙げましょう。

私は動物のクラスと、動物を拡張する犬、魚、猫などを持っています。

を返す3つの異なるメソッドがありMap<String,List<Dog>>, Map<String,List<Fish>>, Map<String,List<Cat>>ます。これらをgetDogMap、getCatMap、およびgetFishMapと呼びます。

私は、さまざまなパラメーターに応じて、これらのメソッドの1つを呼び出すジェネリックメソッドを作成しています。これが私が許可されると期待していたことです:

public void <A extends Animal> doSomething(){

    Map<String,List<A>> someMap;

    if(someCondition){
        someMap = getDogMap();
    }else if(anotherCondition){
        someMap = getFishMap();
    }else{
        someMap = getCatMap():
    }
}

または少なくとも、それはalaをキャストすることですsomeMap = (Map<String,List<Dog>>) getDogMap();

ただし、これは機能しません。Eclipseが教えてくれます"Type mismatch: cannot convert from Map<String,List<Dog>> to Map<String,List<A>>"キャストを強制しようとすると、教えてくれ"Cannot cast from Map<STring,List<Dog>> to Map<String,List<A>>"ます。

私は何が間違っているのですか?

4

2 に答える 2

5

public void <A extends Animal>A「は拡張するタイプである」という意味ではなく、「拡張するタイプの特定の1つであるAnimal」という意味です。次の宣言を使用する必要があります。AAnimal

public void doSomething() {
    Map<String, ? extends List<? extends Animal>>  someMap;
    // ...
}

構成? extends Animalは、「拡張する任意のタイプ」を表現する方法ですAnimal

この宣言を使用する必要がある理由は、直感に反して、ジェネリック型間のサブタイプの関係が通常の型間でどのように機能するかと正確に一致していないためです。たとえばList<Dog>、はのサブタイプですCollection<Dog>。、などのサブタイプではありません。これが許可されない理由は、ヒープ汚染と呼ばれ、 AngelikaLangerのFAQでも説明されています。ただし、はのサブタイプです。タイプの変数には、、、、、またはが割り当てられている可能性があります。重要な部分は、コンパイラがこれらのどれであるかを知らず、ただそれがそれらの1つであるということです。List<Animal>Collection<Animal>List<Dog>List<? extends Animal>List<? extends Animal>List<Dog>List<Cat>List<Animal>

のサブタイプでList<Dog>ないのと同じように、同様に、のサブタイプではないList<Animal>ことも成り立ちます。Map<String, List<Dog>>Map<String, List<? extends Animal>>

ジェネリックがこのように機能する理由を示す最良の方法は、矛盾による証明です。つまり、エラーにつながる(壊れた)コードの例を示すことは、「直感的に」機能するジェネリックでした。したがって、List<Dog>のサブタイプであるList<Animal>場合、次のコードが有効になります。

List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = dogs; // unsafe cast

// this operation violates type safety
animals.add(new Cat());

// would assign a Cat to a variable of type Dog without a compile error!
Dog dog = animals.get(0);

同様に、あなたのためにMap

Map<String, List<Dog>> dogses = new HashMap<String, List<Dog>>();
Map<String, List<? extends Animal>> animalses = dogses; // unsafe cast

List<Cat> cats = new ArrayList();
cats.put(new Cat());
animalses.put("cats", cats);

List<Dog> dogs = dogses.get("cats");
Dog dog = dogs.get(0); // uh-oh
于 2012-08-18T01:06:37.617 に答える
0

ここでの問題は、ジェネリック型の動作方法にあります。

NumberJavaでは、それがのスーパークラスであると期待しIntegerます(そしてあなたは正しいでしょう)。Number[]また、それはのスーパークラスであることが期待されInteger[]ます。あなたも正しいでしょう。List<Number>ただし、それがのスーパークラスであるとは言えませんList<Integer>。これは型安全性に違反します。

List<Integer> intList = new ArrayList<Integer>();
List<Number> numList = intList; // the system stops you here
numList.add(Math.PI); // so that you don't do this

この場合、タイプAnimalとそのサブタイプ、、、DogおよびCatがありFishます。List<Dog>このロジックを動物に拡張すると、にキャストするのに問題がある理由がわかりますList<A>

これで、インターフェースaddAll(Collection)で定義されたメソッドを使用できます。List

List<Animal> animals = new List<Animal>();
List<Dog> dogs = new List<Dog>();
animals.addAll(dogs);
于 2012-08-18T01:09:50.720 に答える