Apakah ada perbedaan di antara keduanya
List<Map<String, String>>
dan
List<? extends Map<String, String>>
?
Jika tidak ada perbedaan, apa manfaat menggunakan ? extends
?
java
generics
inheritance
polymorphism
Eng.Fouad
sumber
sumber
Jawaban:
Perbedaannya adalah bahwa, misalnya, a
adalah
tapi tidak
Begitu:
Anda akan berpikir a
List
dariHashMap
seharusnya aList
dariMap
s, tetapi ada alasan bagus mengapa itu tidak:Misalkan Anda bisa melakukan:
Jadi ini adalah mengapa
List
dariHashMap
s seharusnya tidak menjadiList
dariMap
s.sumber
HashMap
iniMap
karena polimorfisme.List
dariHashMap
s bukanList
dariMap
s.List<Map<String,String>> maps = hashMaps;
danHashMap<String,String> aMap = new HashMap<String, String>();
, Anda masih akan menemukan bahwamaps.add(aMap);
itu ilegal sementarahashMaps.add(aMap);
itu legal. Tujuannya adalah untuk mencegah penambahan jenis yang salah, tetapi itu tidak akan memungkinkan penambahan jenis yang benar (kompiler tidak dapat menentukan jenis "benar" selama waktu kompilasi)HashMap
ke daftarMap
s, kedua contoh Anda legal, jika saya membacanya dengan benar.Anda tidak dapat menetapkan ekspresi dengan tipe seperti
List<NavigableMap<String,String>>
yang pertama.(Jika Anda ingin tahu mengapa Anda tidak dapat menetapkan
List<String>
untukList<Object>
melihat jutaan pertanyaan lain di SO.)sumber
List<String>
bukan subtipe dariList<Object>
? - lihat, misalnya, stackoverflow.com/questions/3246137/…? extends
. Juga tidak menjelaskan korelasi dengan super / subtipe atau co / contravariance (jika ada).Apa yang saya lewatkan dalam jawaban lain adalah referensi tentang bagaimana ini berhubungan dengan co-dan contravariance dan sub- dan supertypes (yaitu, polimorfisme) secara umum dan ke Jawa pada khususnya. Ini mungkin dipahami dengan baik oleh OP, tetapi untuk berjaga-jaga, ini dia:
Kovarian
Jika Anda memiliki kelas
Automobile
, makaCar
danTruck
adalah subtipe mereka. Setiap Mobil dapat ditugaskan ke variabel tipe Mobil, ini terkenal di OO dan disebut polimorfisme. Kovarian mengacu pada penggunaan prinsip yang sama ini dalam skenario dengan obat generik atau delegasi. Java belum memiliki delegasi, jadi istilah ini hanya berlaku untuk obat generik.Saya cenderung menganggap kovarian sebagai polimorfisme standar apa yang Anda harapkan untuk bekerja tanpa berpikir, karena:
Alasan kesalahan adalah, bagaimanapun, benar:
List<Car>
tidak mewarisi dariList<Automobile>
dan dengan demikian tidak dapat ditugaskan satu sama lain. Hanya parameter tipe generik yang memiliki hubungan bawaan. Orang mungkin berpikir bahwa kompiler Java tidak cukup pintar untuk benar memahami skenario Anda di sana. Namun, Anda dapat membantu kompiler dengan memberinya petunjuk:Contravariance
Kebalikan dari co-variance adalah contravariance. Di mana dalam kovarians tipe parameter harus memiliki hubungan subtipe, sebaliknya mereka harus memiliki hubungan supertipe. Ini dapat dianggap sebagai warisan batas atas: supertipe apa pun diizinkan dan termasuk jenis yang ditentukan:
Ini dapat digunakan dengan Collections.sort :
Anda bahkan bisa menyebutnya dengan pembanding yang membandingkan objek dan menggunakannya dengan jenis apa pun.
Kapan menggunakan kontra atau ko-varians?
Mungkin sedikit OT, Anda tidak bertanya, tetapi membantu memahami menjawab pertanyaan Anda. Secara umum, ketika Anda mendapatkan sesuatu, gunakan kovarians dan ketika Anda meletakkan sesuatu, gunakan contravariance. Ini paling baik dijelaskan dalam jawaban untuk pertanyaan Stack Overflow. Bagaimana contravariance digunakan dalam Java generics? .
Jadi dengan apa itu
List<? extends Map<String, String>>
Anda menggunakan
extends
, jadi aturan untuk kovarians berlaku. Di sini Anda memiliki daftar peta dan setiap item yang Anda simpan dalam daftar harus aMap<string, string>
atau berasal dari itu. PernyataanList<Map<String, String>>
tidak dapat diturunkanMap
, tetapi harus berupa aMap
.Karenanya, berikut ini akan berfungsi, karena
TreeMap
mewarisi dariMap
:tetapi ini tidak akan:
dan ini juga tidak akan berhasil, karena tidak memenuhi batasan kovarian:
Apa lagi?
Ini mungkin jelas, tetapi Anda mungkin telah mencatat bahwa menggunakan
extends
kata kunci hanya berlaku untuk parameter itu dan tidak untuk sisanya. Yaitu, yang berikut ini tidak akan dikompilasi:Misalkan Anda ingin mengizinkan jenis apa pun di peta, dengan kunci sebagai string, Anda dapat menggunakan
extend
pada setiap parameter tipe. Yaitu, misalkan Anda memproses XML dan Anda ingin menyimpan AttrNode, Elemen dll di peta, Anda dapat melakukan sesuatu seperti:sumber
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());
hasil dalamfound: ? 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>());
bekerja dengan sempurna. Contoh terakhir jelas benar.Hari ini, saya telah menggunakan fitur ini, jadi inilah contoh kehidupan nyata saya yang sangat segar. (Saya telah mengubah nama kelas dan metode menjadi yang umum sehingga mereka tidak akan mengalihkan perhatian dari titik yang sebenarnya.)
Saya memiliki metode yang dimaksudkan untuk menerima
Set
dariA
benda-benda yang saya awalnya menulis dengan tanda tangan ini:Tetapi ingin benar-benar menyebutnya dengan
Set
s dari subclassA
. Tapi ini tidak diizinkan! (Alasannya adalah,myMethod
bisa menambahkan objek keset
tipeA
, tetapi bukan dari subtipeset
objek yang dinyatakan berada di situs pemanggil. Jadi ini dapat merusak sistem tipe jika memungkinkan.)Sekarang, inilah obat generik untuk menyelamatkan, karena berfungsi seperti yang dimaksudkan jika saya menggunakan metode tanda tangan ini sebagai gantinya:
atau lebih pendek, jika Anda tidak perlu menggunakan tipe aktual di tubuh metode:
Dengan cara ini,
set
tipe menjadi kumpulan objek dari subtipe aktualA
, sehingga menjadi mungkin untuk menggunakan ini dengan subkelas tanpa membahayakan sistem tipe.sumber
Seperti yang Anda sebutkan, mungkin ada dua versi di bawah ini untuk mendefinisikan Daftar:
List<? extends Map<String, String>>
List<?>
2 sangat terbuka. Itu dapat menampung semua jenis objek. Ini mungkin tidak berguna jika Anda ingin memiliki peta jenis tertentu. Jika seseorang secara tidak sengaja meletakkan jenis peta yang berbeda, misalnya
Map<String, int>
,. Metode konsumen Anda mungkin rusak.Untuk memastikan bahwa
List
dapat menampung objek dari tipe tertentu, Java generics diperkenalkan? extends
. Jadi di # 1,List
bisa memegang objek apa pun yang berasal dariMap<String, String>
tipe. Menambahkan jenis data apa pun akan membuat pengecualian.sumber