20

私はこれを回避するのに苦労していて、誰かがこれの理由を説明できるかどうか疑問に思っていました。

私には3つのクラスがあります:

class Angel {}
class Person extends Angel {}
class Employee extends Person {}

このコードを実行しようとすると

public static void insertElements(List<? super Person> list){
    list.add(new Person());
    list.add(new Employee());
    list.add(new Angel());
}

エラーが発生します:

The method add(capture#5-of ? super Person) in the type List<capture#5-of ? super Person> is not applicable for the arguments (Angel)

私は常にドキュメントを読んで、<? super X >任意のタイプのXまたはXのスーパークラス(私の場合、AngelはPersonのスーパークラス)を意味し、許可する必要がありますか?明らかに違います!

簡単な例はありますか?

4

5 に答える 5

31

あなたの直感的なロジックは、「aList<? super Person>は、またはのスーパータイプであるもののリストであるPersonPersonため、当然、それに追加することができAngelます」と言います。その解釈は間違っています。

宣言は、それがリストに追加されることを可能にするようなタイプになることをList<? super Person> list保証します。はではないので、これは当然コンパイラによって許可されません。を使用してメソッドを呼び出すことを検討してください。そのようなリストにを追加しても大丈夫でしょうか?絶対にありません。listPersonAngel PersoninsertElements(new ArrayList<Person>)Angel

それについて推論する最良の方法List<? super Person>は、明確な型ではないということです。これは、引数として許可される型の範囲を記述するパターンです。List<Person>のサブタイプでList<? super Person>はなく、このパターンに一致するタイプと見なしてください。許可される操作はList<? super Person>、任意の一致するタイプで許可される操作です。

于 2012-11-29T10:05:10.907 に答える
16

私にとって、これらの答えはどれも私を助けてくれましたが、十分に明確ではありませんでした。よく調べた後、つまりたくさんのことを言った後、私はついにワイルドカードの最も簡単な説明を思いつきました。

public class CatTest {

    public class Animal {}
    public class Cat extends Animal {}
    public class MyCat extends Cat {}
    public class Dog extends Animal {}

    public static void main(String[] args) {
        List<Animal> animalList = new ArrayList<>();
        List<Cat> catList = new ArrayList<>();
        List<MyCat> myCatList = new ArrayList<>();
        List<Dog> dogList = new ArrayList<>();

        CatTest catTest = new CatTest();
        // Here you are trying to add a MyCat instance (in the addMethod we create a MyCat instance). MyCat is a Cat, Cat is an Animal, therefore MyCat is an Animal.
        // So you can add a new MyCat() to a list of List<Animal> because it's a list of Animals and MyCat IS an Animal.
        catTest.addMethod(animalList);

        // Here you are trying to add a MyCat instance (in the addMethod we create a MyCat instance). MyCat is a Cat. 
        // So you can add a new MyCat() to a list of List<Cat> because it is a list of Cats, and MyCat IS a Cat
        catTest.addMethod(catList);

        // Here you are trying to add a MyCat instance (in the addMethod we create a MyCat instance). MyCat is a Cat.
        // Everything should work but the problem here is that you restricted (bounded) the type of the lists to be passed to the method to be of
        // a type that is either "Cat" or a supertype of "Cat". While "MyCat" IS a "Cat". It IS NOT a supertype of "Cat". Therefore you cannot use the method
        catTest.addMethod(myCatList); // Doesn't compile

        // Here you are adding a MyCat instance (in the addMethod we create a MyCat instance). MyCat is a Cat. 
        // You cannot call the method here, because "Dog" is not a "Cat" or a supertype of "Cat"
        catTest.addMethod(dogList); // Doesn't compile
    }

    public void addMethod(List<? super Cat> catList) {
        // Adding MyCat works since MyCat is a subtype of whatever type of elements catList contains
        // (for example Cat, Animal, or Object)
        catList.add(new MyCat());
        System.out.println("Cat added");
    }
}

最後に、これらは結論です:

ワイルドカードを使用する場合、ワイルドカードは、要素をリストに追加しようとしたときに要素のタイプではなく、メソッドに引数として渡されるリストのタイプに適用されます。この例では、次のコンパイルエラーが発生します。

タイプCatTestのメソッドaddMethod(List)は、引数(List)には適用できません。

ご覧のとおり、エラーはメソッドのシグネチャに関するものであり、メソッドの本体に関するものではありません。したがって、「Cat」または「Cat」のスーパータイプ()のいずれかである要素のリストのみを渡すことができますList<Animal>, List<Cat>

特定のタイプの要素を含むリストを渡すと、リストに追加できるのは「Cat」または「Cat」のサブタイプの要素のみです。つまり、要素のコレクションがある場合は通常どおりに動作します。 !!「猫」のリストに「動物」を追加することはできません。前に述べたように、ワイルドカードは要素自体には適用されず、制限は「リスト」にのみ適用されます。さて、それはなぜですか?単純で、明白で、よく知られている理由のために:

Animal animal = new Dog();

「猫」リストに「動物」を追加できる場合は、「犬」(「犬」は「動物」)を追加することもできますが、「猫」ではありません。

于 2015-11-24T14:38:59.880 に答える
2

私がいつも行っていたように厳しい理解があり、確実にするためにList<? super Person>導入され、上限のワイルドカードが提供する柔軟性である引数として両方を渡すことができます。List<Person>List<Angel>

ただし、メソッド内では、PersonまたはのサブタイプのみPersonをリストに挿入できます。これにより、リストに常に有効なインスタンスが含まれるようになります。

たとえば、のリストを渡す場合はPerson、にのみ自動タイプキャストされている間EmployeePerson挿入できます。EmployeePerson

于 2019-03-11T06:26:48.403 に答える
0

逆に考えてみてください。にバインドされた型の場合List<? super Person>List<Person>は明らかにパラメータの有効な型です。コンパイラがコードを許可している場合は、あるタイプのコードをに挿入できAngelますList<Person>

于 2012-11-29T10:07:22.043 に答える
0

ワイルドカードを使用すると、基本的に、パラメータのタイプ範囲を指定できます。

public static void insertElements(List<? super Person> list)、コンパイラを使用すると、Personまたはそのスーパータイプのいずれかのリストを渡すことができます。具体的な例を見てみましょう

List<Object> objects = new ArrayList<>();
List<Angel> angels = new ArrayList<>();
List<Person> persons = new ArrayList<>();
List<Employee> employees = new ArrayList<>();

insertElements(objects);      // Works fine, Object is super type of every type
insertElements(angels);       // Works fine, Angel is super type of Person
insertElements(persons);      // Works fine, Person itself
insertElements(employees);    // Error, Employee is neither Person itself nor its a super type of Person

では、なぜそのエラーが発生するのかを見てみましょう

上記のコードから、人物、天使、またはオブジェクトのリストをに渡すことができ、正常に機能することがわかりますinsertElements。次に、人物のリストを使用してメソッドを呼び出すとします。だからリストは今List<Person>ですそしてあなたはlist.add(Angel());

Person person = new Angel();これは基本的に、この割り当てを行おうとするとタイプ互換ではなく、エラーが発生することを意味します。

于 2021-06-24T03:33:40.287 に答える