Arasında herhangi bir fark var mı
List<Map<String, String>>
ve
List<? extends Map<String, String>>
?
Fark yoksa kullanmanın faydası ? extends
nedir?
Arasında herhangi bir fark var mı
List<Map<String, String>>
ve
List<? extends Map<String, String>>
?
Fark yoksa kullanmanın faydası ? extends
nedir?
Yanıtlar:
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 List
ait HashMap
s bir olmalı List
ait Map
değ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 List
bir HashMap
saniye bir olmamalı List
ait Map
s.
HashMap
bir olan Map
polimorfizm nedeniyle.
List
of HashMap
s, a List
of Map
s değil .
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)
HashMap
listesine Map
onları doğru okuyorum eğer örnekler ikisinde yasal, s.
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ı.)
List<String>
alt türü değil List<Object>
mi? - bkz., örneğin, stackoverflow.com/questions/3246137/…
? extends
. Süper / alt tipler veya ortak / kontravaryans (eğer varsa) ile olan ilişkiyi de açıklamaz.
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:
Bir sınıfınız varsa Automobile
, o zaman Car
ve Truck
onları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
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.
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? .
List<? extends Map<String, String>>
Sen kullanmak extends
iç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 TreeMap
miras 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
Bu muhtemelen açıktır, ancak extends
anahtar 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>());
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.
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 Set
ve A
ben başlangıçta bu imza ile yazdığı nesneler:
void myMethod(Set<A> set)
Ama aslında onu Set
alt sınıflarının s'leri ile adlandırmak istiyor A
. Ancak buna izin verilmez! (Bunun nedeni, bu türe myMethod
nesneler ekleyebilir , ancak nesnelerin arayanın sitesinde olduğu bildirilen alt set
türden A
değ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.
Bahsettiğiniz gibi, bir Listeyi tanımlamanın aşağıdaki iki versiyonu olabilir:
List<? extends Map<String, String>>
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 List
belirli türdeki nesneleri tutabilmesini sağlamak için Java jenerikleri tanıtıldı ? extends
. Yani # 1'de, türden List
türetilen herhangi bir nesneyi tutabilir Map<String, String>
. Başka herhangi bir veri türü eklemek bir istisna oluşturacaktır.