Saya membaca tentang metode umum dari OracleDocGenericMethod . Saya cukup bingung tentang perbandingannya ketika dikatakan kapan harus menggunakan wild-card dan kapan harus menggunakan metode umum. Mengutip dari dokumen.
interface Collection<E> { public boolean containsAll(Collection<?> c); public boolean addAll(Collection<? extends E> c); }
Kami dapat menggunakan metode umum di sini sebagai gantinya:
interface Collection<E> { public <T> boolean containsAll(Collection<T> c); public <T extends E> boolean addAll(Collection<T> c); // Hey, type variables can have bounds too! }
[…] Ini memberitahu kita bahwa argumen type digunakan untuk polimorfisme; efek satu-satunya adalah mengizinkan berbagai jenis argumen aktual untuk digunakan di situs pemanggilan yang berbeda. Jika demikian, gunakan karakter pengganti. Karakter pengganti dirancang untuk mendukung subtipe fleksibel, yang kami coba ungkapkan di sini.
Bukankah menurut kami wild card (Collection<? extends E> c);
juga mendukung jenis polimorfisme? Lalu mengapa penggunaan metode generik dianggap kurang baik dalam hal ini?
Melanjutkan ke depan, itu menyatakan,
Metode generik memungkinkan parameter tipe digunakan untuk mengekspresikan ketergantungan di antara tipe satu atau lebih argumen ke metode dan / atau tipe kembaliannya. Jika tidak ada ketergantungan seperti itu, metode umum tidak boleh digunakan.
Apa artinya ini?
Mereka telah mempresentasikan contohnya
class Collections { public static <T> void copy(List<T> dest, List<? extends T> src) { ... }
[…]
Kita bisa saja menulis tanda tangan untuk metode ini dengan cara lain, tanpa menggunakan wildcard sama sekali:
class Collections { public static <T, S extends T> void copy(List<T> dest, List<S> src) { ... }
Dokumen tersebut mencegah deklarasi kedua dan mempromosikan penggunaan sintaks pertama? Apa perbedaan antara deklarasi pertama dan kedua? Keduanya sepertinya melakukan hal yang sama?
Adakah yang bisa menjelaskan area ini.
?
sama sekali. Anda dapat menulis ulang sebagai `public static <T1 extends Number, T2 extends Number> void copy (List <T1> dest, List <T2> src) dan dalam hal ini menjadi jelas apa yang sedang terjadi.List
menggunakan parameter type.List<T super Integer>
tidak valid, dan tidak akan dikompilasi.<T extends X & Y>
-> banyak batas.Pertimbangkan contoh berikut dari The Java Programming oleh James Gosling edisi ke-4 di bawah ini di mana kami ingin menggabungkan 2 SinglyLinkQueue:
Kedua metode di atas memiliki fungsi yang sama. Jadi mana yang lebih disukai? Jawabannya adalah yang kedua. Dengan kata-kata penulis sendiri:
"Aturan umumnya adalah menggunakan karakter pengganti jika Anda bisa karena kode dengan karakter pengganti umumnya lebih mudah dibaca daripada kode dengan beberapa parameter tipe. Saat memutuskan apakah Anda memerlukan variabel tipe, tanyakan pada diri Anda apakah variabel tipe itu digunakan untuk menghubungkan dua atau lebih parameter, atau untuk menghubungkan tipe parameter dengan tipe kembalian. Jika jawabannya tidak, maka karakter pengganti harus mencukupi. "
Catatan: Dalam buku hanya metode kedua yang diberikan dan nama parameter tipe adalah S, bukan 'T'. Metode pertama tidak ada di buku.
sumber
Dalam pertanyaan pertama Anda: Ini berarti bahwa jika ada hubungan antara tipe parameter dan tipe kembalian metode, maka gunakan generik.
Sebagai contoh:
Di sini Anda mengekstrak beberapa T mengikuti kriteria tertentu. Jika T
Long
metode Anda akan kembaliLong
danCollection<Long>
; tipe kembalian aktual bergantung pada tipe parameter, oleh karena itu berguna, dan disarankan, untuk menggunakan tipe generik.Jika tidak demikian, Anda dapat menggunakan jenis kartu liar:
Dalam dua contoh ini, apa pun tipe item dalam koleksi, tipe pengembaliannya adalah
int
danboolean
.Dalam contoh Anda:
kedua fungsi tersebut akan mengembalikan boolean apa pun jenis item dalam koleksi. Dalam kasus kedua, ini terbatas pada instance dari subclass E.
Pertanyaan kedua:
Kode pertama ini memungkinkan Anda untuk mengirimkan heterogen
List<? extends T> src
sebagai parameter. Daftar ini dapat berisi banyak elemen dari kelas yang berbeda selama semuanya memperluas kelas dasar T.jika Anda memiliki:
dan
Anda bisa melakukannya
Di samping itu
membatasi
List<S> src
menjadi satu kelas tertentu S yang merupakan subkelas dari T. Daftar hanya dapat berisi elemen dari satu kelas (dalam contoh ini S) dan tidak ada kelas lain, bahkan jika mereka mengimplementasikan T juga. Anda tidak akan dapat menggunakan contoh saya sebelumnya tetapi Anda dapat melakukan:sumber
List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
Bukan sintaks yang valid. Anda harus membuat instance ArrayList tanpa batas.ArrayList(Collection<? extends E> c)
. Bisakah Anda menjelaskan mengapa Anda mengatakan itu?Metode wildcard juga umum - Anda bisa menyebutnya dengan beberapa tipe.
The
<T>
sintaks mendefinisikan nama tipe variabel. Jika variabel tipe memiliki kegunaan apa pun (misalnya dalam implementasi metode atau sebagai batasan untuk tipe lain), maka masuk akal untuk menamainya, jika tidak, Anda dapat menggunakan?
, sebagai variabel anonim. Jadi, sepertinya hanya jalan pintas.Selain itu,
?
sintaksis tidak dapat dihindari saat Anda mendeklarasikan bidang:sumber
Saya akan mencoba menjawab pertanyaan Anda, satu per satu.
Tidak. Alasannya adalah karakter pengganti terikat tidak memiliki jenis parameter yang ditentukan. Itu tidak diketahui. Semua yang "diketahui" adalah bahwa "penahanan" adalah tipe
E
(apa pun yang didefinisikan). Jadi, ini tidak dapat memverifikasi dan membenarkan apakah nilai yang diberikan cocok dengan tipe yang dibatasi.Jadi, tidak masuk akal untuk memiliki perilaku polimorfik pada karakter pengganti.
Pilihan pertama lebih baik dalam kasus ini karena
T
selalu dibatasi, dansource
pasti akan memiliki nilai (tidak diketahui) yang subclassT
.Jadi, misalkan Anda ingin menyalin semua daftar nomor, opsi pertama adalah
src
, pada dasarnya, dapat menerimaList<Double>
,,List<Float>
dll. karena ada batas atas tipe parameter yang ditemukan didest
.Opsi kedua akan memaksa Anda untuk mengikat
S
untuk setiap jenis yang ingin Anda salin, seperti ituAs
S
adalah tipe berparameter yang membutuhkan pengikatan.Saya harap ini membantu.
sumber
<S extends T>
menyatakan bahwaS
adalah tipe berparameter yang merupakan subkelas dariT
, sehingga memerlukan tipe berparameter (tanpa karakter pengganti) yang merupakan subkelasT
.Satu perbedaan lain yang tidak tercantum di sini.
Tetapi hal berikut akan mengakibatkan kesalahan waktu kompilasi.
sumber
Sejauh yang saya mengerti, hanya ada satu kasus penggunaan ketika wildcard sangat dibutuhkan (yaitu dapat mengekspresikan sesuatu yang tidak dapat Anda ungkapkan menggunakan parameter tipe eksplisit). Ini adalah saat Anda perlu menentukan batas bawah.
Selain itu, karakter pengganti berfungsi untuk menulis kode yang lebih ringkas, seperti yang dijelaskan oleh pernyataan berikut dalam dokumen yang Anda sebutkan:
sumber
Terutama -> Wildcard memberlakukan generik pada level parameter / argumen dari metode Non-Generik. Catatan. Ini juga dapat dilakukan di genericMethod secara default, tetapi di sini, bukan? kita bisa menggunakan T itu sendiri.
paket obat generik;
SO wildcard memiliki kegunaan khusus seperti ini.
sumber