Liste <Eşleme <Dize, Dize >> - Liste <? Harita <Dize, Dize >> öğesini genişletir


129

Arasında herhangi bir fark var mı

List<Map<String, String>>

ve

List<? extends Map<String, String>>

?

Fark yoksa kullanmanın faydası ? extendsnedir?


2
Javayı seviyorum ama bu o kadar da iyi olmayan şeylerden biri ...
Mite Mitreski

4
Bence onu "genişleyen herhangi bir şey ..." gibi okursak, netleşir.
Garbage

İnanılmaz, yaklaşık 3 günde 12.000'den fazla görüntüleme? !!
Eng.Fouad

5
Hacker News ön sayfasına ulaştı. Tebrikler.
r3st0r3

1
@ Eng. Burada bulundu. Artık ön sayfada değil, dün oradaydı. news.ycombinator.net/item?id=3751901 (dün burada Hindistan'da Pazar ortası.)
r3st0r3

Yanıtlar:


180

Aradaki fark, örneğin bir

List<HashMap<String,String>>

bir

List<? extends Map<String,String>>

ama değil

List<Map<String,String>>

Yani:

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
}

Bir düşünürdüm Listait HashMaps bir olmalı Listait Mapdeğil mi neden s, ama iyi bir nedeni var:

Yapabileceğinizi varsayalım:

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)

Bir Demek bu yüzden Listbir HashMapsaniye bir olmamalı Listait Maps.


6
Yine de, HashMapbir olan Mappolimorfizm nedeniyle.
Eng.Fouad

46
Doğru, ama a Listof HashMaps, a Listof Maps değil .
gerçeklik


İyi örnek. Ayrıca kayda değer Gümrüğe bile olmasıdır List<Map<String,String>> maps = hashMaps; ve HashMap<String,String> aMap = new HashMap<String, String>();bunu yine bulacaksınız maps.add(aMap);yasadışı süre olduğunu hashMaps.add(aMap);yasaldır. Amaç, yanlış türlerin eklenmesini önlemektir, ancak doğru türlerin eklenmesine izin vermez (derleyici, derleme sırasında "doğru" türü belirleyemez)
Raze

Hayır @Raze, aslında olabilir bir ekleme HashMaplistesine Maponları doğru okuyorum eğer örnekler ikisinde yasal, s.
gerçeklik

24

List<NavigableMap<String,String>>İlki gibi türlere sahip ifadeler atayamazsınız .

(Eğer atanamıyor neden bilmek istiyorsanız List<String>için List<Object>bir bakın milyonlarca SO diğer soruları.)


1
daha fazla açıklayabilir mi? anlayamıyorum veya iyi uygulama için herhangi bir bağlantı?
Samir Mangroliya

3
@Samir Neyi açıklayabilirim? List<String>alt türü değil List<Object>mi? - bkz., örneğin, stackoverflow.com/questions/3246137/…
Tom Hawtin - tackline

2
Bu, olan ve olmayan arasındaki farkın ne olduğunu açıklamıyor ? extends. Süper / alt tipler veya ortak / kontravaryans (eğer varsa) ile olan ilişkiyi de açıklamaz.
Abel

16

Diğer cevaplarda eksik olduğum şey, bunun genel olarak ortak ve kontravarlık ile alt ve üst türlerle (yani polimorfizm) ve özelde Java ile nasıl ilişkili olduğuna dair bir referanstır. Bu, OP tarafından iyi anlaşılabilir, ancak her ihtimale karşı, işte burada:

Kovaryans

Bir sınıfınız varsa Automobile, o zaman Carve Truckonların alt türleri. Herhangi bir Araba, Otomobil türünün bir değişkenine atanabilir, bu OO'da iyi bilinir ve polimorfizm olarak adlandırılır. Kovaryans, aynı prensibi jenerikler veya temsilcilerle senaryolarda kullanmayı ifade eder. Java'nın temsilcileri yok (henüz), bu nedenle bu terim yalnızca jenerikler için geçerlidir.

Kovaryansı standart polimorfizm olarak düşünme eğilimindeyim, düşünmeden çalışmasını beklediğiniz şey, çünkü:

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

Bununla birlikte, hatanın nedeni doğrudur: kalıtımsal List<Car> değildirList<Automobile> ve bu nedenle birbirlerine atanamaz. Yalnızca genel tür parametrelerinin devralma ilişkisi vardır. Java derleyicisinin oradaki senaryonuzu doğru bir şekilde anlayacak kadar akıllı olmadığı düşünülebilir. Ancak, derleyiciye bir ipucu vererek yardımcı olabilirsiniz:

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

contravariance

Eş varyansın tersi kontravarlıktır. Kovaryansta, parametre türlerinin bir alt tür ilişkisine sahip olması gerektiği yerde, tersine bir üst tür ilişkisine sahip olmaları gerekir. Bu, kalıtımın üst sınırı olarak düşünülebilir: belirtilen tür dahil olmak üzere herhangi bir üst türe izin verilir:

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

Bu, Collections.sort ile kullanılabilir :

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

Hatta onu nesneleri karşılaştıran ve herhangi bir türle kullanan bir karşılaştırıcıyla arayabilirsin.

Kontra veya eş varyans ne zaman kullanılır?

Belki biraz fazla uzadı, sormadınız, ama sorunuzu cevaplamayı anlamanıza yardımcı olur. Genel olarak, ne zaman olsun bir şey, kullanım kovaryans ve ne zaman koymak bir şey, kullanım contravariance. Bu, en iyi Yığın Taşması sorusunun cevabında açıklanır . Java jeneriklerinde kontravans nasıl kullanılır? .

O zaman ne var List<? extends Map<String, String>>

Sen kullanmak extendsiçin, bu yüzden kuralları kovaryans uygular. Burada bir harita listesi var ve listede sakladığınız her öğe bir Map<string, string>veya ondan türetilmiş olmalıdır . Deyim List<Map<String, String>>türetmek olamaz Map, ancak olmalıdır bir Map .

Dolayısıyla, aşağıdakilerden TreeMapmiras aldığı için aşağıdakiler çalışacaktır Map:

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

ama bu olmayacak:

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

ve bu da işe yaramayacak çünkü kovaryans kısıtlamasını karşılamıyor:

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

Başka?

Bu muhtemelen açıktır, ancak extendsanahtar kelimeyi kullanmanın yalnızca o parametre için geçerli olduğunu ve geri kalanı için geçerli olmadığını fark etmiş olabilirsiniz. Yani, aşağıdakiler derlenmeyecek:

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

Dize olarak bir anahtar ile haritada herhangi bir türe izin vermek istediğinizi varsayalım extend, her tür parametresinde kullanabilirsiniz. Yani, XML'i işlediğinizi ve AttrNode, Element vb. Bir haritada saklamak istediğinizi varsayalım, şöyle bir şey yapabilirsiniz:

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

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

"Öyleyse o zaman ne var ..." içindeki hiçbir şey derlenmez.
NobleUplift

@NobleUplift: Aldığınız hata mesajını vermezseniz size yardımcı olmak zordur. Alternatif olarak, cevabınızı almak için SO'da yeni bir soru sormayı düşünün, daha büyük başarı şansı. Yukarıdaki kod sadece parçacıklardır, senaryonuzda uygulamanıza bağlıdır.
Abel

Yeni bir sorum yok, geliştirmelerim var. List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());sonuçlanır 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>());Mükemmel çalışıyor. Son örnek açıkça doğrudur.
NobleUplift

@NobleUplift: Üzgünüm, uzun süre seyahat ediyorum, geri döndüğümde düzelecek ve göze batan hatayı işaret ettiğiniz için teşekkürler! :)
Abel

Sorun değil, düzeltileceğine sevindim. Onaylamak isterseniz düzenlemeleri sizin için yaptım.
NobleUplift

4

Bugün bu özelliği kullandım, işte benim çok taze gerçek hayat örneğim. (Sınıf ve yöntem adlarını genel olanlarla değiştirdim, böylece asıl noktadan dikkati dağıtmayacaklar.)

Ben kabul için yaratılmış bir yöntem var Setve Aben başlangıçta bu imza ile yazdığı nesneler:

void myMethod(Set<A> set)

Ama aslında onu Setalt sınıflarının s'leri ile adlandırmak istiyor A. Ancak buna izin verilmez! (Bunun nedeni, bu türe myMethodnesneler ekleyebilir , ancak nesnelerin arayanın sitesinde olduğu bildirilen alt settürden Adeğil set. Bu, mümkünse tür sistemini bozabilir.)

Şimdi kurtarmaya jenerikler geliyor, çünkü bunun yerine bu yöntem imzasını kullanırsam amaçlandığı gibi çalışıyor:

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

yöntem gövdesinde gerçek türü kullanmanız gerekmiyorsa veya daha kısa:

void myMethod(Set<? extends A> set)

Bu şekilde, set's türü, öğesinin gerçek alt türünün nesnelerinin bir koleksiyonu haline gelir A, böylece bunu alt sınıflarla tür sistemini tehlikeye atmadan kullanmak mümkün hale gelir.


0

Bahsettiğiniz gibi, bir Listeyi tanımlamanın aşağıdaki iki versiyonu olabilir:

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

2 çok açık. Herhangi bir nesne türünü tutabilir. Belirli türde bir haritaya sahip olmak istemeniz durumunda bu kullanışlı olmayabilir. Örneğin, birinin yanlışlıkla farklı türde bir harita koyması durumunda Map<String, int>. Tüketici yönteminiz bozulabilir.

Bunun Listbelirli türdeki nesneleri tutabilmesini sağlamak için Java jenerikleri tanıtıldı ? extends. Yani # 1'de, türden Listtüretilen herhangi bir nesneyi tutabilir Map<String, String>. Başka herhangi bir veri türü eklemek bir istisna oluşturacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.