Apakah mungkin untuk mendapatkan jenis parameter generik?
Sebuah contoh:
public final class Voodoo {
public static void chill(List<?> aListWithTypeSpiderMan) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = ???;
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>());
}
}
java
generics
reflection
cimnine
sumber
sumber
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
tidak yakin apa kendalanya.java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
Saya ingin mencoba menjabarkan jawaban dari @DerMike untuk menjelaskan:
Pertama, tipe erasure tidak berarti bahwa JDK dihilangkan informasi tipe saat runtime. Ini adalah metode untuk memungkinkan kompatibilitas jenis waktu kompilasi dan jenis runtime untuk hidup berdampingan dalam bahasa yang sama. Seperti yang disiratkan oleh blok kode ini, JDK menyimpan informasi tipe yang terhapus - hanya saja tidak terkait dengan gips dan barang yang diperiksa.
Kedua, ini memberikan informasi jenis generik ke kelas generik tepat satu tingkat di atas hirarki dari jenis beton yang diperiksa - yaitu kelas induk abstrak dengan parameter tipe generik dapat menemukan jenis beton yang sesuai dengan parameter jenisnya untuk implementasi beton itu sendiri. yang secara langsung mewarisi darinya. Jika kelas ini non-abstrak dan instantiated, atau implementasi konkret adalah dua tingkat turun, ini tidak akan berhasil (walaupun sedikit jimmying dapat membuatnya berlaku untuk jumlah level yang telah ditentukan di luar satu, atau naik ke kelas terendah dengan parameter tipe X generik, dan lain-lain).
Pokoknya, lanjut ke penjelasan. Ini kodenya lagi, dipisahkan menjadi beberapa baris untuk memudahkan referensi:
Mari 'kita' menjadi kelas abstrak dengan tipe generik yang berisi kode ini. Bacalah ini dengan kasar dari dalam ke luar:
... dan itu sudah cukup. Jadi kami mendorong info jenis dari implementasi konkret kami sendiri kembali ke diri kami sendiri, dan menggunakannya untuk mengakses pegangan kelas. kita bisa menggandakan getGenericSuperclass () dan naik dua level, atau menghilangkan getGenericSuperclass () dan mendapatkan nilai untuk diri kita sendiri sebagai tipe konkret (peringatan: Saya belum menguji skenario ini, mereka belum mendatangi saya).
Akan menjadi rumit jika anak-anak Anda yang konkret menjadi jumlah hop yang sewenang-wenang pergi, atau jika Anda konkret dan tidak final, dan terutama rumit jika Anda mengharapkan anak-anak Anda (yang sangat dalam) memiliki obat generik sendiri. Tetapi Anda biasanya dapat mendesain pertimbangan-pertimbangan itu, jadi ini yang paling membantu Anda.
Semoga ini bisa membantu seseorang! Saya tahu posting ini kuno. Saya mungkin akan memotong penjelasan ini dan menyimpannya untuk pertanyaan lain.
sumber
Sebenarnya saya berhasil ini. Pertimbangkan cuplikan berikut:
Saya menggunakan jdk 1.6
sumber
Sebenarnya ada solusi, dengan menerapkan trik "kelas anonim" dan ide-ide dari Token Jenis Super :
Trik terletak pada penciptaan kelas anonim ,
new ArrayList<SpiderMan>() {}
, di tempat aslinya (sederhana)new ArrayList<SpiderMan>()
. Penggunaan kelas anoymous (jika mungkin) memastikan bahwa kompiler menyimpan informasi tentang argumen tipe yangSpiderMan
diberikan ke parameter tipeList<?>
. Voa!sumber
Karena jenis penghapusan, satu-satunya cara untuk mengetahui jenis daftar adalah dengan mengirimkan jenis sebagai parameter ke metode:
sumber
Lampiran untuk jawaban @ DerMike untuk mendapatkan parameter generik dari antarmuka yang diparameterisasi (menggunakan metode #getGenericInterfaces () di dalam metode default Java-8 untuk menghindari duplikasi):
sumber
Awesomeable<Beer>
. Dalam hal itu, informasi jenis disimpan. Jika Anda meneruskannew Awesomable<Beer> ()
ke metode wt tidak akan berfungsi.Awesomable<Beer>
dengan cepat tanpa definisi eksplisit dari subkelas beton sepertiEstrellaGalicia
dalam kasus ini, masih mengeluarkan tipe parameter: Saya menjalankannya sekarang:System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());
---> Jenisnya adalah: ParameterizedStuff $ BeerTidak, itu tidak mungkin. Karena masalah kompatibilitas ke bawah, generik Java didasarkan pada penghapusan tipe , ia saat runtime, semua yang Anda miliki adalah objek non-generik
List
. Ada beberapa informasi tentang parameter tipe saat runtime, tetapi ia berada di definisi kelas (yaitu Anda dapat bertanya " tipe generik apa yang digunakan definisi bidang ini? "), Bukan dalam instance objek.sumber
Seperti yang ditunjukkan oleh @bertolami, kita tidak mungkin mendapatkan tipe variabel dan mendapatkan nilainya di masa depan (konten dari variabel typeOfList).
Namun demikian, Anda dapat melewatkan kelas sebagai parameter di atasnya seperti ini:
Itu kurang lebih apa yang dilakukan Google ketika Anda harus melewatkan variabel kelas ke konstruktor ActivityInstrumentationTestCase2 .
sumber
Tidak itu tidak mungkin.
Anda bisa mendapatkan jenis bidang generik yang diberikan kelas adalah satu-satunya pengecualian untuk aturan itu dan bahkan itu sedikit peretasan.
Lihat Mengetahui jenis generik di Jawa untuk contohnya.
sumber
Anda bisa mendapatkan jenis parameter generik dengan refleksi seperti dalam contoh ini yang saya temukan di sini :
sumber
Jawaban cepat atas Pertanyaannya adalah Anda tidak dapat melakukannya, karena penghapusan tipe generik Java.
Jawaban yang lebih panjang adalah jika Anda telah membuat daftar seperti ini:
Maka dalam hal ini tipe generik dipertahankan dalam superclass generik dari kelas anonim baru di atas.
Bukannya saya merekomendasikan melakukan ini dengan daftar, tetapi ini adalah implementasi pendengar:
Dan karena mengekstrapolasi tipe generik dari kelas super dan antarmuka super berubah di antara JVM, solusi generiknya tidak semaju yang disarankan oleh beberapa jawaban.
Di sini sekarang saya melakukannya.
sumber
Ini tidak mungkin karena obat generik di Jawa hanya dianggap pada waktu kompilasi. Jadi, generik Java hanyalah semacam pra-prosesor. Namun Anda bisa mendapatkan kelas aktual dari anggota daftar.
sumber
Saya telah mengkodekan ini untuk metode yang ingin menerima atau kembali
Iterable<?...>
. Ini kodenya:sumber
Anda tidak bisa mendapatkan parameter generik dari variabel. Tapi Anda bisa dari deklarasi metode atau bidang:
sumber
Bagi saya membaca potongan kode ini sulit, saya hanya membaginya menjadi 2 baris yang dapat dibaca:
Saya ingin membuat instance dari parameter Type tanpa memiliki parameter apa pun pada metode saya:
sumber
Ini trik lain. Gunakan array vararg generik
sumber
Saya perhatikan bahwa banyak orang condong ke arah
getGenericSuperclass()
solusi:Namun, solusi ini rawan kesalahan. Ini tidak akan berfungsi dengan baik jika ada obat generik di keturunan. Pertimbangkan ini:
Jenis apa yang akan
Bar.persistentClass
dimiliki?Class<Integer>
? Tidak akanClass<Double>
. Ini akan terjadi karenagetClass()
selalu mengembalikan kelas paling atas, yangBar
dalam hal ini, dan kelas super generiknyaFoo<Double>
. Oleh karena itu, tipe argumennya adalahDouble
.Jika Anda membutuhkan solusi andal yang tidak gagal saya dapat menyarankan dua.
Guava
. Ia memiliki kelas yang dibuat tepat untuk tujuan ini:com.google.common.reflect.TypeToken
. Ini menangani semua kasus sudut dengan baik dan menawarkan beberapa fungsionalitas yang lebih bagus. Downside adalah ketergantungan ekstra. Karena Anda telah menggunakan kelas ini, kode Anda akan terlihat sederhana dan jelas, seperti ini:sumber
Menggunakan:
sumber
toArray()
kembaliObject[]
. Anda tidak dapat dan tidak seharusnya bergantung padanya sebagai subtipe tertentu.