Saya menemukan kode Java seperti ini:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
Apa perbedaan di antara ketiga hal di atas dan apa yang mereka sebut jenis deklarasi kelas atau antarmuka di Jawa?
Saya menemukan kode Java seperti ini:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
Apa perbedaan di antara ketiga hal di atas dan apa yang mereka sebut jenis deklarasi kelas atau antarmuka di Jawa?
Jawaban:
Yah tidak ada perbedaan antara dua yang pertama - mereka hanya menggunakan nama yang berbeda untuk parameter tipe ( E
atau T
).
Yang ketiga bukan deklarasi yang valid - ?
digunakan sebagai wildcard yang digunakan saat memberikan argumen tipe , mis. List<?> foo = ...
Sarana yang foo
merujuk ke daftar jenis tertentu, tapi kami tidak tahu apa.
Semua ini adalah obat generik , yang merupakan topik yang cukup besar. Anda mungkin ingin mempelajarinya melalui sumber daya berikut, walaupun tentu saja ada lebih banyak tersedia:
T
dan E
- mereka hanya pengidentifikasi. Anda bisa menulis KeyValuePair<K, V>
misalnya. ?
memiliki makna khusus.
Itu lebih banyak konvensi daripada yang lainnya.
T
dimaksudkan untuk menjadi Tipe E
dimaksudkan untuk menjadi Elemen ( List<E>
: daftar Elemen) K
adalah Kunci (dalam a Map<K,V>
) V
adalah Nilai (sebagai nilai balik atau nilai yang dipetakan) Mereka sepenuhnya dipertukarkan (konflik dalam deklarasi yang sama meskipun).
Jawaban sebelumnya menjelaskan parameter tipe (T, E, dll.), Tetapi jangan menjelaskan wildcard, "?", Atau perbedaan di antara mereka, jadi saya akan membahasnya.
Pertama, untuk memperjelas: wildcard dan parameter type tidak sama. Di mana parameter tipe mendefinisikan semacam variabel (misalnya, T) yang mewakili tipe untuk cakupan, wildcard tidak: wildcard hanya mendefinisikan sekumpulan tipe yang diijinkan yang dapat Anda gunakan untuk tipe generik. Tanpa ada pembatas ( extends
atau super
), wildcard berarti "gunakan jenis apa pun di sini".
Wildcard selalu datang di antara kurung sudut, dan itu hanya memiliki makna dalam konteks tipe generik:
public void foo(List<?> listOfAnyType) {...} // pass a List of any type
tidak pernah
public <?> ? bar(? someType) {...} // error. Must use type params here
atau
public class MyGeneric ? { // error
public ? getFoo() { ... } // error
...
}
Semakin membingungkan di mana mereka tumpang tindih. Sebagai contoh:
List<T> fooList; // A list which will be of type T, when T is chosen.
// Requires T was defined above in this scope
List<?> barList; // A list of some type, decided elsewhere. You can do
// this anywhere, no T required.
Ada banyak tumpang tindih dalam apa yang mungkin dengan definisi metode. Berikut ini adalah, secara fungsional, identik:
public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething) {...}
Jadi, jika ada tumpang tindih, mengapa menggunakan yang satu atau yang lain? Kadang-kadang, itu hanya gaya: beberapa orang mengatakan bahwa jika Anda tidak memerlukan param tipe, Anda harus menggunakan wildcard hanya untuk membuat kode lebih sederhana / lebih mudah dibaca. Satu perbedaan utama yang saya jelaskan di atas: tipe params mendefinisikan variabel tipe (misalnya, T) yang dapat Anda gunakan di tempat lain dalam ruang lingkup; wildcard tidak. Kalau tidak, ada dua perbedaan besar antara tipe params dan wildcard:
Tipe params dapat memiliki beberapa kelas pembatas; wildcard tidak dapat:
public class Foo <T extends Comparable<T> & Cloneable> {...}
Wildcard dapat memiliki batas bawah; jenis params tidak dapat:
public void bar(List<? super Integer> list) {...}
Di atas List<? super Integer>
didefinisikan Integer
sebagai batas bawah pada wildcard, artinya tipe Daftar harus Integer atau tipe super Integer. Pembatasan tipe generik melampaui apa yang ingin saya bahas secara rinci. Singkatnya, ini memungkinkan Anda untuk menentukan jenis tipe generik. Hal ini memungkinkan untuk mengobati obat generik secara polimorfik. Misalnya dengan:
public void foo(List<? extends Number> numbers) {...}
Anda dapat lulus List<Integer>
, List<Float>
, List<Byte>
, dll untuk numbers
. Tanpa batasan jenis, ini tidak akan berhasil - itu hanya bagaimana generik.
Akhirnya, inilah definisi metode yang menggunakan wildcard untuk melakukan sesuatu yang saya pikir Anda tidak bisa melakukan cara lain:
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
numberSuper.add(elem);
}
numberSuper
dapat berupa Daftar Nomor atau supertipe Nomor (misalnya, List<Object>
), dan elem
harus Nomor atau subtipe apa pun. Dengan semua lompatan, kompiler dapat yakin bahwa itu .add()
adalah typesafe.
Variabel tipe, <T>, bisa berupa tipe non-primitif apa pun yang Anda tentukan: tipe kelas apa pun, tipe antarmuka apa pun, tipe array apa pun, atau bahkan variabel tipe lain.
Nama parameter tipe yang paling umum digunakan adalah:
Di Java 7 diizinkan untuk instantiate seperti ini:
Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6
Nama parameter tipe yang paling umum digunakan adalah:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
Anda akan melihat nama-nama ini digunakan di seluruh Java SE API
kompiler akan membuat tangkapan untuk setiap wildcard (misalnya, tanda tanya di Daftar) ketika membuat fungsi seperti:
foo(List<?> list) {
list.put(list.get()) // ERROR: capture and Object are not identical type.
}
Namun tipe generik seperti V akan baik-baik saja dan menjadikannya metode generik :
<V>void foo(List<V> list) {
list.put(list.get())
}