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


129

違いはありますか

List<Map<String, String>>

そして

List<? extends Map<String, String>>

違いがない場合、使用するメリットは何? extendsですか?


2
私は、javaを愛するが、これは...良いことではないものの一つです
ダニMitreski

4
「拡張するものは何でも…」と読めばわかると思います。
ゴミ

信じられないほど、約3日間で12,000回以上再生されましたか?!!
Eng.Fouad

5
それはハッカーニュースのフロントページに達しました。おめでとうございます。
r3st0r3 2012年

1
@ Eng.Foundここにあります。もはやトップページではなく、昨日そこにありました。news.ycombinator.net/item?id=3751901(昨日は日曜日の真ん中の日曜日です)
r3st0r3

回答:


180

違いは、たとえば、

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
}

あなたは思うだろうListHashMapsがあるべきListMapSが、それはない正当な理由があります:

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

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)

なぜこれがあるListHashMapsがあってはならないListMap秒。


6
それでも、HashMapあるMap多型に起因します。
Eng.Fouad

46
右、しかし、ListHashMapsがないListMap秒。
真実性


良い例え。また、注目に値するのは、を宣言List<Map<String,String>> maps = hashMaps; した場合でも、合法であるHashMap<String,String> aMap = new HashMap<String, String>();にもかかわらず、それmaps.add(aMap);が違法であることに気付くことですhashMaps.add(aMap);。目的は間違った型の追加を防ぐことですが、正しい型の追加は許可されません(コンパイラーはコンパイル時に「正しい」型を判別できません)
Raze

@Razeいいえ、実際にをのリストに追加できます。正​​しく読んでいれば、どちらの例も合法です。HashMapMap
真実性2012年

24

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

(SOに関する膨大な数の他の質問List<String>List<Object>表示するように割り当てることができない理由を知りたい場合。)


1
さらに説明できますか?私は理解できないか、良い実践のためのリンクはありませんか?
Samir Mangroliya 2012年

3
@サミール説明する?List<String>のサブタイプではありませんList<Object>か?-たとえば、stackoverflow.com
/ questions / 3246137 /…を

2
これは、の有無による違いを説明していません? extends。また、スーパー/サブタイプまたは共分散(存在する場合)との相関関係についても説明していません。
アベル

16

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

共分散

あなたはクラスを持っている場合はAutomobile、その後、CarおよびTruckそのサブタイプです。自動車はタイプAutomobileの変数に割り当てることができます。これはオブジェクト指向ではよく知られており、ポリモーフィズムと呼ばれます。共分散とは、ジェネリックまたはデリゲートを使用するシナリオでこれと同じ原則を使用することを指します。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> 継承されないため、List<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はおそらく質問しなかったかもしれませんが、質問への回答を理解するのに役立ちます。一般に、何かを取得する場合は共分散を使用し、何かを配置する場合は反変を使用します。これは、スタックオーバーフローの質問への回答で最もよく説明されます。Javaジェネリックスではどのように反変が使用されますか?

それでそれで何ですか List<? extends Map<String, String>>

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

したがって、以下からTreeMap継承するため、次のように機能しますMap

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>());

「それで、それでそれで何であるか...」の何もコンパイルされません。
NobleUplift 2013

@NobleUplift:表示されるエラーメッセージを提供しないと、あなたを助けるのは困難です。また、選択肢として、SOで新しい質問をして答えを得ると、成功の可能性が高まります。上記のコードは単なるスニペットであり、シナリオに実装したかどうかに依存します。
アベル

新しい質問はありません。改善があります。List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());結果はfound: ? extends java.util.Map<java.lang.String,java.lang.String> required: class or interface without boundsです。List<Map<String, String>> mapList = new ArrayList<Map<String, String>>(); mapList.add(new TreeMap<String, String>());完璧に動作します。最後の例は明らかに正しいです。
NobleUplift 2013

@NobleUplift:申し訳ありませんが、長い旅行で、戻ってきたときに修正されます。目立つエラーを指摘していただきありがとうございます。:)
アベル

問題ありません。修正されるだけでよかったです。あなたがそれらを承認したいのであれば、私はあなたのために編集を行いました。
NobleUplift 2013

4

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

を受け入れることを意図したメソッドがSetありますA最初にこのシグネチャで書いたオブジェクトのます:

void myMethod(Set<A> set)

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

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

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

メソッド本体で実際の型を使用する必要がない場合は、以下のようにします。

void myMethod(Set<? extends A> set)

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


0

あなたが言及したように、リストを定義する以下の2つのバージョンがある可能性があります:

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.