Ini muncul sebagai pertanyaan yang saya ajukan dalam sebuah wawancara baru-baru ini karena kandidat ingin melihat ditambahkan ke bahasa Jawa. Ini biasanya diidentifikasi sebagai masalah bahwa Java tidak memiliki obat generik , tetapi ketika didorong, kandidat tidak dapat benar-benar memberi tahu saya hal-hal yang dapat dia capai jika mereka ada di sana.
Jelas karena tipe mentah diperbolehkan di Java (dan pemeriksaan tidak aman), adalah mungkin untuk menumbangkan obat generik dan berakhir dengan List<Integer>
yang (misalnya) sebenarnya berisi String
s. Ini jelas bisa dibuat mustahil jika informasi jenis direifikasi; tapi pasti ada lebih dari ini !
Dapatkah orang memposting contoh hal-hal yang benar-benar ingin mereka lakukan , apakah obat generik reified tersedia? Maksud saya, jelas Anda bisa mendapatkan tipe a List
saat runtime - tetapi apa yang akan Anda lakukan dengannya?
public <T> void foo(List<T> l) {
if (l.getGenericType() == Integer.class) {
//yeah baby! err, what now?
EDIT : Pembaruan cepat untuk ini karena jawabannya tampaknya terutama berkaitan dengan kebutuhan untuk memasukkan a Class
sebagai parameter (misalnya EnumSet.noneOf(TimeUnit.class)
). Saya mencari lebih banyak hal di sepanjang garis di mana hal ini tidak mungkin . Sebagai contoh:
List<?> l1 = api.gimmeAList();
List<?> l2 = api.gimmeAnotherList();
if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
l1.addAll(l2); //why on earth would I be doing this anyway?
sumber
findClass()
harus mengabaikan parameterisasi, tetapidefineClass()
tidak bisa). Dan seperti yang kita ketahui, The Powers That Be memegang sangat penting kompatibilitas.Jawaban:
Dari beberapa kali saya menemukan "kebutuhan" ini, akhirnya bermuara pada konstruksi ini:
Ini berfungsi di C # dengan asumsi bahwa
T
memiliki konstruktor default . Anda bahkan bisa mendapatkan jenis runtimetypeof(T)
dan mendapatkan konstruktornyaType.GetConstructor()
.Solusi umum Java adalah meneruskan
Class<T>
argumen as.(itu tidak perlu dilewatkan sebagai argumen konstruktor, karena argumen metode juga baik-baik saja, di atas hanyalah sebuah contoh, juga
try-catch
dihilangkan agar singkatnya)Untuk semua konstruksi tipe umum lainnya, tipe sebenarnya dapat dengan mudah diselesaikan dengan sedikit bantuan refleksi. T&J di bawah mengilustrasikan kasus dan kemungkinan penggunaan:
sumber
T.class
atauT.getClass()
, sehingga Anda bisa mengakses semua bidang, konstruktor, dan metodenya. Itu membuat konstruksi juga tidak mungkin.Hal yang paling sering mengganggu saya adalah ketidakmampuan untuk memanfaatkan beberapa pengiriman di beberapa jenis generik. Hal berikut ini tidak mungkin dan ada banyak kasus di mana itu akan menjadi solusi terbaik:
sumber
public void my_method(List input) {}
. Namun saya tidak pernah menemukan kebutuhan ini, hanya karena mereka tidak memiliki nama yang sama. Jika mereka memiliki nama yang sama, saya akan mempertanyakan apakahpublic <T extends Object> void my_method(List<T> input) {}
bukan ide yang lebih baik.myStringsMethod(List<String> input)
danmyIntegersMethod(List<Integer> input)
bahkan jika overloading untuk kasus seperti itu mungkin terjadi di Java.<algorithm>
di C ++.Keamanan jenis muncul dalam pikiran. Downcasting ke tipe parametrized akan selalu tidak aman tanpa reified generics:
Selain itu, abstraksi akan lebih sedikit bocor - setidaknya abstraksi yang mungkin tertarik dengan informasi runtime tentang parameter tipenya. Saat ini, jika Anda memerlukan informasi runtime jenis apa pun tentang jenis salah satu parameter umum, Anda juga harus meneruskannya
Class
. Dengan begitu, antarmuka eksternal Anda bergantung pada implementasi Anda (apakah Anda menggunakan RTTI tentang parameter Anda atau tidak).sumber
ParametrizedList
salinan data dalam jenis pemeriksaan koleksi sumber. Ini agak miripCollections.checkedList
tetapi dapat diunggulkan dengan koleksi untuk memulai.T.class.getAnnotation(MyAnnotation.class)
(whereT
is a generic type) tanpa mengubah antarmuka eksternal.Anda akan dapat membuat array umum dalam kode Anda.
sumber
String[] strings = new String[1]; Object[] objects = strings; objects[0] = new Object();
Mengompilasi dengan baik dalam kedua bahasa. Berjalan tidak baik.Ini adalah pertanyaan lama, ada banyak sekali jawaban, tetapi saya pikir jawaban yang ada melenceng.
"reified" hanya berarti nyata dan biasanya hanya berarti kebalikan dari penghapusan tipe.
Masalah besar terkait dengan Java Generics:
void method(List<A> l)
danmethod(List<B> l)
. Hal ini karena penghapusan tipe tetapi sangat kecil.sumber
deserialize(thingy, List<Integer>.class)
Serialisasi akan lebih mudah dengan reifikasi. Yang kami inginkan adalah
Yang harus kita lakukan adalah
terlihat jelek dan bekerja dengan funky.
Ada juga kasus di mana akan sangat membantu untuk mengatakan sesuatu seperti
Hal-hal ini tidak sering menggigit, tetapi akan menggigit saat terjadi.
sumber
Paparan saya tentang Java Geneircs sangat terbatas, dan terlepas dari poin-poin yang telah disebutkan oleh jawaban lain, terdapat skenario yang dijelaskan dalam buku Java Generics and Collections , oleh Maurice Naftalin dan Philip Walder, di mana obat generik yang direifikasi berguna.
Karena jenis tidak dapat diubah, tidak mungkin memiliki pengecualian berparameter.
Misalnya pernyataan formulir di bawah ini tidak valid.
Ini karena klausa catch memeriksa apakah pengecualian yang ditampilkan cocok dengan tipe yang diberikan. Pemeriksaan ini sama dengan pemeriksaan yang dilakukan oleh uji contoh dan karena jenisnya tidak dapat diubah, bentuk pernyataan di atas tidak valid.
Jika kode di atas valid maka penanganan pengecualian dengan cara di bawah ini akan dimungkinkan:
Buku tersebut juga menyebutkan bahwa jika generik Java didefinisikan mirip dengan cara kerangka C ++ didefinisikan (perluasan), hal itu dapat mengarah pada penerapan yang lebih efisien karena ini menawarkan lebih banyak peluang untuk pengoptimalan. Tetapi tidak menawarkan penjelasan lebih dari ini, jadi penjelasan (petunjuk) dari orang-orang yang berpengetahuan akan sangat membantu.
sumber
Array mungkin akan bermain jauh lebih baik dengan obat generik jika mereka direifikasi.
sumber
List<String>
bukanList<Object>
.Saya memiliki pembungkus yang menyajikan jdbc resultet sebagai iterator, (itu berarti saya dapat menguji unit operasi yang berasal dari database jauh lebih mudah melalui injeksi ketergantungan).
API terlihat seperti di
Iterator<T>
mana T adalah beberapa tipe yang dapat dibangun hanya dengan menggunakan string dalam konstruktor. Iterator kemudian melihat string yang dikembalikan dari kueri sql dan kemudian mencoba mencocokkannya dengan konstruktor tipe T.Dengan cara generik saat ini diimplementasikan, saya juga harus meneruskan kelas objek yang akan saya buat dari resultet saya. Jika saya mengerti dengan benar, jika obat generik direifikasi, saya bisa memanggil T.getClass () mendapatkan konstruktornya, dan kemudian tidak perlu menampilkan hasil Class.newInstance (), yang akan jauh lebih rapi.
Pada dasarnya, saya pikir itu membuat penulisan API (daripada hanya menulis aplikasi) lebih mudah, karena Anda dapat menyimpulkan lebih banyak dari objek, dan dengan demikian lebih sedikit konfigurasi yang diperlukan ... Saya tidak menghargai implikasi anotasi sampai saya melihat mereka digunakan dalam hal-hal seperti spring atau xstream sebagai ganti rim config.
sumber
Satu hal yang menyenangkan adalah menghindari tinju untuk tipe primitif (nilai). Ini agak terkait dengan keluhan larik yang diajukan orang lain, dan dalam kasus di mana penggunaan memori dibatasi, hal itu sebenarnya dapat membuat perbedaan yang signifikan.
Ada juga beberapa jenis masalah saat menulis kerangka di mana kemampuan untuk merefleksikan tipe berparameter itu penting. Tentu saja ini bisa diatasi dengan melewatkan objek kelas pada saat runtime, tapi ini mengaburkan API dan menempatkan beban tambahan pada pengguna framework.
sumber
new T[]
mana T adalah tipe primitif!Bukan berarti Anda akan mencapai sesuatu yang luar biasa. Ini hanya akan lebih sederhana untuk dipahami. Penghapusan jenis sepertinya merupakan waktu yang sulit bagi pemula, dan pada akhirnya membutuhkan pemahaman seseorang tentang cara kerja compiler.
Pendapat saya adalah, bahwa obat generik hanyalah tambahan yang menghemat banyak pengecoran yang berlebihan.
sumber
Sesuatu yang terlewatkan oleh semua jawaban di sini yang selalu memusingkan saya adalah karena jenisnya dihapus, Anda tidak dapat mewarisi antarmuka generik dua kali. Ini bisa menjadi masalah saat Anda ingin membuat antarmuka yang halus.
sumber
Inilah salah satu yang menarik perhatian saya hari ini: tanpa reifikasi, jika Anda menulis metode yang menerima daftar vararg dari item umum ... penelepon dapat BERPIKIR bahwa mereka aman untuk mengetik, tetapi secara tidak sengaja memasukkan bahan mentah lama, dan meledakkan metode Anda.
Sepertinya tidak mungkin itu akan terjadi? ... Tentu, sampai ... Anda menggunakan Kelas sebagai tipe data Anda. Pada titik ini, pemanggil Anda dengan senang hati akan mengirimi Anda banyak objek Kelas, tetapi kesalahan ketik sederhana akan mengirimi Anda objek Kelas yang tidak sesuai dengan T, dan bencana melanda.
(NB: Saya mungkin telah membuat kesalahan di sini, tapi mencari-cari di sekitar "varargs generik", di atas tampaknya hanya apa yang Anda harapkan. Hal yang membuat masalah ini praktis adalah penggunaan Kelas, saya pikir - penelepon tampaknya kurang hati-hati :()
Misalnya, saya menggunakan paradigma yang menggunakan objek Kelas sebagai kunci dalam peta (ini lebih kompleks daripada peta sederhana - tetapi secara konseptual itulah yang terjadi).
misalnya ini berfungsi dengan baik di Java Generics (contoh sepele):
misalnya tanpa reifikasi di Java Generics, yang ini menerima objek "Kelas" APA PUN. Dan itu hanya sebagian kecil dari kode sebelumnya:
Metode di atas harus ditulis ribuan kali dalam satu proyek - sehingga kemungkinan terjadinya kesalahan manusia menjadi tinggi. Kesalahan debugging terbukti "tidak menyenangkan". Saat ini saya mencoba mencari alternatif, tapi jangan berharap banyak.
sumber