web-dev-qa-db-ja.com

List <Map <String、String >> vs List <? Map <String、String >>を拡張します

間に違いはありますか

List<Map<String, String>>

そして

List<? extends Map<String, String>>

違いがない場合、? extendsを使用する利点は何ですか?

125
Eng.Fouad

違いは、たとえば、

List<HashMap<String,String>>

List<? extends Map<String,String>>

しかしではない

List<Map<String,String>>

そう:

void withWilds( List<? extends Map<String,String>> foo ){}
void noWilds( List<Map<String,String>> foo ){}

void main( String[] args ){
    List<HashMap<String,String>> myMap;

    withWilds( myMap ); // Works
    noWilds( myMap ); // Compiler error
}

ListHashMapListMapであると考えられますが、そうではない理由があります。

あなたができると仮定します:

List<HashMap<String,String>> hashMaps = new ArrayList<HashMap<String,String>>();

List<Map<String,String>> maps = hashMaps; // Won't compile,
                                          // but imagine that it could

Map<String,String> aMap = Collections.singletonMap("foo","bar"); // Not a HashMap

maps.add( aMap ); // Perfectly legal (adding a Map to a List of Maps)

// But maps and hashMaps are the same object, so this should be the same as

hashMaps.add( aMap ); // Should be illegal (aMap is not a HashMap)

そのため、List of HashMapsをList of Mapsにしないでください。

176
trutheality

List<NavigableMap<String,String>>などのタイプの式を最初に割り当てることはできません。

List<String>List<Object>に割り当てられない理由を知りたい場合は、SOでzillionの他の質問を参照してください。)

25

私が他の答えに欠けているのは、これが一般に共変および反変、サブタイプおよびスーパータイプ(つまり、ポリモーフィズム)と特にJavaにどのように関係するかについての参照です。これはOPによってよく理解されるかもしれませんが、念のため、ここで説明します。

共分散

クラスAutomobileがある場合、CarTruckはサブタイプです。任意のCarをAutomobile型の変数に割り当てることができます。これはOOでよく知られており、ポリモーフィズムと呼ばれます。共分散とは、ジェネリックまたはデリゲートのシナリオでこの同じ原則を使用することを指します。 Javaには(まだ)デリゲートがないため、この用語はジェネリックのみに適用されます。

私は共分散を標準的なポリモーフィズムと考える傾向があります。何も考えずに機能すると期待されるものです。

List<Car> cars;
List<Automobile> automobiles = cars;
// You'd expect this to work because Car is-a Automobile, but
// throws inconvertible types compile error.

ただし、エラーの理由は正しいです:List<Car>does notList<Automobile>を継承するため、互いに割り当てることはできません。ジェネリック型パラメーターのみに継承関係があります。 Javaコンパイラーは、そこでのシナリオを適切に理解するのに十分なほどスマートではないと考えるかもしれません。ただし、コンパイラーにヒントを与えることで、コンパイラーを支援できます。

List<Car> cars;
List<? extends Automobile> automobiles = cars;   // no error

共分散

共分散の逆は反分散です。共分散ではパラメータータイプはサブタイプの関係を持つ必要がありますが、反分散ではスーパータイプの関係が必要です。これは、継承の上限と見なすことができます。指定されたタイプを含む、すべてのスーパータイプが許可されます。

class AutoColorComparer implements Comparator<Automobile>
    public int compare(Automobile a, Automobile b) {
        // Return comparison of colors
    }

これは、 Collections.sort で使用できます。

public static <T> void sort(List<T> list, Comparator<? super T> c)

// Which you can call like this, without errors:
List<Car> cars = getListFromSomewhere();
Collections.sort(cars, new AutoColorComparer());

オブジェクトを比較し、任意のタイプで使用する比較器で呼び出すこともできます。

いつコントラ分散または共分散を使用しますか?

少しOTおそらくあなたは尋ねなかったが、それはあなたの質問への答えを理解するのに役立つ。一般に、get何かの場合は共分散を使用し、put何かの場合は反分散を使用します。これは、 Stack Overflowの質問への回答Javaジェネリックスで反分散をどのように使用しますか? で最もよく説明されています。

では、List<? extends Map<String, String>>の場合はどうなりますか

extendsを使用するため、covarianceのルールが適用されます。ここにはマップのリストがあり、リストに保存する各アイテムはMap<string, string>であるか、そこから派生している必要があります。ステートメントList<Map<String, String>>Mapから派生できませんが、aMapでなければなりません。

したがって、TreeMapMapを継承するため、以下が機能します。

List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
mapList.add(new TreeMap<String, String>());

しかし、これはしません:

List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new TreeMap<String, String>());

共分散制約を満たさないため、これも機能しません。

List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new ArrayList<String>());   // This is NOT allowed, List does not implement Map

ほかに何か?

これはおそらく明らかですが、extendsキーワードの使用はそのパラメーターにのみ適用され、残りには適用されないことに既に気付いているかもしれません。つまり、以下はコンパイルされません:

List<? extends Map<String, String>> mapList = new List<? extends Map<String, String>>();
mapList.add(new TreeMap<String, Element>())  // This is NOT allowed

文字列としてキーを使用して、マップ内の任意のタイプを許可する場合、各タイプパラメーターでextendを使用できます。つまり、XMLを処理していて、AttrNode、Elementなどをマップに保存したい場合、次のようなことができます。

List<? extends Map<String, ? extends Node>> listOfMapsOfNodes = new...;

// Now you can do:
listOfMapsOfNodes.add(new TreeMap<Sting, Element>());
listOfMapsOfNodes.add(new TreeMap<Sting, CDATASection>());
16
Abel

今日、私はこの機能を使用しているので、ここに私の非常に新鮮な実際の例があります。 (クラスとメソッドの名前を一般的なものに変更したので、実際のポイントから気を散らすことはありません。)

私はこの署名で最初に書いたSet of Aオブジェクトを受け入れることを意図したメソッドを持っています:

void myMethod(Set<A> set)

しかし、SetのサブクラスのAsで実際に呼び出したいのです。しかし、これは許可されていません! (その理由は、myMethodset型のオブジェクトをAに追加できますが、setのオブジェクトが呼び出し側にあると宣言されているサブタイプではありませんそのため、可能であれば型システムを壊す可能性があります。)

ここで、ジェネリックが助けになります。このメソッドシグネチャを代わりに使用すると、意図したとおりに機能するためです。

<T extends A> void myMethod(Set<T> set)

または、メソッド本体で実際の型を使用する必要がない場合:

void myMethod(Set<? extends A> set)

このように、setの型はAの実際のサブタイプのオブジェクトのコレクションになるため、型システムを危険にさらすことなくサブクラスでこれを使用することが可能になります。

4
Rörd

あなたが述べたように、リストを定義する以下の2つのバージョンがあります。

  1. List<? extends Map<String, String>>
  2. List<?>

2は非常にオープンです。任意のオブジェクトタイプを保持できます。これは、特定のタイプのマップが必要な場合には役に立たない場合があります。誰かが誤って別のタイプのマップ、たとえばMap<String, int>を配置した場合に備えて。コンシューマーメソッドが壊れる可能性があります。

Listが特定のタイプのオブジェクトを保持できるようにするために、Javaジェネリックが? extendsを導入しました。したがって、#1では、ListMap<String, String>タイプから派生した任意のオブジェクトを保持できます。他のタイプのデータを追加すると、例外がスローされます。

0