Apa perbedaan antara List
, List<?>
, List<T>
, List<E>
, dan List<Object>
?
1. Daftar
List
: adalah jenis mentah, oleh karena itu tidak typesafe
. Itu hanya akan menghasilkan kesalahan runtime ketika casting buruk. Kami ingin kesalahan waktu kompilasi ketika para pemain buruk. Tidak disarankan untuk digunakan.
2. Daftar <?>
List<?>
adalah wildcard tak terbatas. Tapi saya tidak yakin untuk apa ini? Saya dapat mencetak List<?>
tanpa masalah:
public static void test(List<?> list){
System.out.println(list); // Works
}
Mengapa saya tidak bisa menambahkan item ke List<?>
?
public static void test(List<?> list){
list.add(new Long(2)); // Error
list.add("2"); // Error
System.out.println(list);
}
3. Daftar <T>
public static void test(List<T> list){ // T cannot be resolved
System.out.println(list);
}
Saya tidak mengerti sintaks ini. Saya melihat sesuatu seperti ini, dan berhasil:
public <T> T[] toArray(T[] a){
return a;
}
Kadang-kadang, saya melihat <T>
, atau <E>
, atau <U>
, <T,E>
. Apakah mereka semua sama atau mereka mewakili sesuatu yang berbeda?
4. Daftar <Object>
Ini memberikan kesalahan "Metode test(List<Object>)
ini tidak berlaku untuk argumen List<String>
":
public static void test(List<Object> list){
System.out.println(list);
}
Jika saya mencoba ini, maka saya mendapat "Tidak dapat mengirim dari List<String>
ke List<Object>
":
test((List<Object>) names);
Saya bingung. String
adalah subkelas dari Object
, jadi mengapa bukan List<String>
subkelas dari List<Object>
?
2
. Saya menulis beberapa kode untuk menunjukkan ini di2
. tyvmList<Object>
?Untuk bagian terakhir: Meskipun String adalah bagian dari Objek, tetapi Daftar <String> tidak diwarisi dari Daftar <Object>.
sumber
List<Object>
?Notasi
List<?>
berarti "daftar sesuatu (tapi saya tidak mengatakan apa)". Karena kode dalamtest
karya untuk segala jenis objek dalam daftar, ini berfungsi sebagai parameter metode formal.Menggunakan parameter type (seperti pada poin 3 Anda), mengharuskan parameter type dideklarasikan. Sintaks Java untuk itu adalah untuk meletakkan
<T>
di depan fungsi. Ini persis analog dengan mendeklarasikan nama parameter formal ke metode sebelum menggunakan nama-nama di tubuh metode.Mengenai
List<Object>
tidak menerima aList<String>
, itu masuk akal karena aString
tidakObject
; ini adalah subclass dariObject
. Cara mengatasinya adalah mendeklarasikanpublic static void test(List<? extends Object> set) ...
. Tetapi kemudianextends Object
itu mubazir, karena setiap kelas meluas secara langsung atau tidak langsungObject
.sumber
List<Object>
?List<?>
karena daftar tersebut adalah jenis tertentu tetapi tidak diketahui.List<Object>
akan benar-benar menjadi "daftar apa saja" karena memang bisa berisi apa saja.Alasan Anda tidak dapat melemparkan
List<String>
untukList<Object>
adalah bahwa hal itu akan memungkinkan Anda untuk melanggar kendala dariList<String>
.Pikirkan tentang skenario berikut: Jika saya punya
List<String>
, itu seharusnya hanya berisi objek bertipeString
. (Yang merupakanfinal
kelas)Jika saya bisa melemparkan itu ke
List<Object>
, maka itu memungkinkan saya untuk menambahObject
daftar itu, sehingga melanggar kontrak asliList<String>
.Jadi, secara umum, jika kelas
C
mewarisi dari kelasP
, Anda tidak bisa mengatakan ituGenericType<C>
juga mewarisi dariGenericType<P>
.NB Saya sudah mengomentari ini dalam jawaban sebelumnya tetapi ingin mengembangkannya.
sumber
List<Object>
?List<Object>
karena itu semacam mengalahkan tujuan generik. Namun, ada beberapa kasus di mana kode lama mungkinList
menerima jenis yang berbeda, jadi Anda mungkin ingin memperbaiki kode untuk menggunakan parameterisasi jenis hanya untuk menghindari peringatan kompiler untuk jenis mentah. (Tapi fungsinya tidak berubah)Saya akan menyarankan membaca teka-teki Jawa. Ini menjelaskan warisan, generik, abstraksi, dan wildcard dalam deklarasi dengan cukup baik. http://www.javapuzzlers.com/
sumber
Mari kita bicara tentang mereka dalam konteks sejarah Jawa;
List
:Daftar berarti dapat memasukkan Obyek apa pun. Daftar itu dalam rilis sebelum Java 5.0; Java 5.0 memperkenalkan Daftar, untuk kompatibilitas ke belakang.
List<?>
:?
berarti Obyek yang tidak diketahui, bukan Objek apa pun;?
pengantar wildcard adalah untuk memecahkan masalah yang dibangun oleh Generic Type; lihat wildcard ; tetapi ini juga menyebabkan masalah lain:List< T> List< E>
Berarti Deklarasi generik dengan premis tipe T atau E di Lib proyek Anda.
List< Object>
berarti parameterisasi generik.sumber
Pada poin ketiga Anda, "T" tidak dapat diselesaikan karena tidak dideklarasikan, biasanya ketika Anda mendeklarasikan kelas generik Anda dapat menggunakan "T" sebagai nama parameter tipe terikat , banyak contoh online termasuk
tutorial oracle yangmenggunakan "T" sebagai nama parameter type, misalnya, Anda mendeklarasikan kelas seperti:Anda mengatakan bahwa
FooHandler's
operateOnFoo
metode mengharapkan variabel tipe "T" yang dideklarasikan pada deklarasi kelas itu sendiri, dengan mengingat hal ini, Anda kemudian dapat menambahkan metode lain sepertidalam semua kasus baik T, E atau U di sana semua pengidentifikasi parameter tipe, Anda bahkan dapat memiliki lebih dari satu tipe parameter yang menggunakan sintaksis
di ponint keempat Anda meskipun secara efektif Sting adalah sub jenis Objek, di kelas generik tidak ada hubungan seperti itu,
List<String>
bukan sub jenisList<Object>
mereka adalah dua jenis yang berbeda dari sudut pandang kompiler, ini paling baik dijelaskan dalam entri blog inisumber
Teori
String[]
dapat dilemparkan keObject[]
tapi
List<String>
tidak dapat dilemparkan keList<Object>
.Praktek
Untuk daftar itu lebih halus dari itu, karena pada waktu kompilasi jenis parameter Daftar yang diteruskan ke metode tidak dicentang. Definisi metode mungkin juga mengatakan
List<?>
- dari sudut pandang kompiler itu setara. Inilah sebabnya mengapa contoh OP # 2 memberikan kesalahan runtime bukan mengkompilasi kesalahan.Jika Anda menangani
List<Object>
parameter yang diteruskan ke metode dengan hati-hati sehingga Anda tidak memaksakan pemeriksaan tipe pada elemen apa pun dari daftar, maka Anda dapat menentukan metode Anda menggunakanList<Object>
tetapi sebenarnya menerimaList<String>
parameter dari kode panggilan.A. Jadi kode ini tidak akan memberikan kesalahan kompilasi atau runtime dan akan benar-benar (dan mungkin mengejutkan?):
B. Kode ini akan memberikan kesalahan runtime:
C. Kode ini akan memberikan kesalahan runtime (
java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]
):Dalam B, parameter
set
bukan yang diketikList
pada waktu kompilasi: kompilator melihatnya sebagaiList<?>
. Ada kesalahan runtime karena pada saat runtime,set
menjadi objek aktual yang dilewatimain()
, dan itu adalah aList<String>
. AList<String>
tidak bisa dilemparkan keList<Object>
.Di C, parameter
set
membutuhkan sebuahObject[]
. Tidak ada kesalahan kompilasi dan tidak ada kesalahan runtime ketika dipanggil denganString[]
objek sebagai parameter. Itu karenaString[]
dilemparkan keObject[]
. Tetapi objek yang sebenarnya diterima olehtest()
tetap aString[]
, itu tidak berubah. Jadiparams
objek juga menjadi aString[]
. Dan elemen 0 dariString[]
tidak dapat ditugaskan keLong
!(Mudah-mudahan saya memiliki semuanya di sini, jika alasan saya salah, saya yakin komunitas akan memberi tahu saya. DIPERBARUI: Saya telah memperbarui kode dalam contoh A sehingga benar-benar dikompilasi, sambil tetap menunjukkan poin yang dibuat.)
sumber
List<Object> cannot be applied to List<String>
. Anda tidak dapat meneruskanArrayList<String>
ke metode yang diharapkanArrayList<Object>
.Masalah 2 OK, karena "System.out.println (set);" berarti "System.out.println (set.toString ());" set adalah turunan dari Daftar, jadi pengompil akan memanggil List.toString ();
Masalah 3: simbol-simbol ini sama, tetapi Anda dapat memberikan spesifikasi yang berbeda. Sebagai contoh:
Masalah 4: Koleksi tidak memungkinkan kovarian parameter parameter. Tetapi array memungkinkan kovarians.
sumber
Anda benar: String adalah bagian dari Object. Karena String lebih "tepat" daripada Object, Anda harus membuatnya untuk menggunakannya sebagai argumen untuk System.out.println ().
sumber