Misalnya, katakanlah Anda memiliki dua kelas:
public class TestA {}
public class TestB extends TestA{}
Saya memiliki metode yang mengembalikan a List<TestA>
dan saya ingin melemparkan semua objek dalam daftar itu TestB
sehingga saya berakhir dengan List<TestB>
.
casting generik tidak dimungkinkan, tetapi jika Anda mendefinisikan daftar dengan cara lain, dimungkinkan untuk menyimpan TestB di dalamnya:
Anda masih harus memeriksa jenis yang harus dilakukan ketika Anda menggunakan objek dalam daftar.
sumber
Cannot instantiate the type ArrayList<? extends TestA>
.Anda benar-benar tidak dapat *:
Contoh diambil dari tutorial Java ini
Asumsikan ada dua jenis
A
danB
semacamnyaB extends A
. Maka kode berikut ini benar:Kode sebelumnya valid karena
B
merupakan subclass dariA
. Sekarang, apa yang terjadi denganList<A>
danList<B>
?Ternyata itu
List<B>
bukan subklasList<A>
karena itu kami tidak bisa menulisTerlebih lagi, kita bahkan tidak bisa menulis
*: Untuk memungkinkan casting, kami membutuhkan orang tua yang sama untuk keduanya
List<A>
danList<B>
:List<?>
misalnya. Berikut ini valid:Namun, Anda akan mendapat peringatan. Anda dapat menekannya dengan menambahkan
@SuppressWarnings("unchecked")
metode Anda.sumber
Dengan Java 8, Anda sebenarnya bisa
sumber
new List<TestB>
, loop dan gips?Saya pikir Anda melemparkan ke arah yang salah meskipun ... jika metode mengembalikan daftar
TestA
objek, maka itu benar-benar tidak aman untuk melemparkannyaTestB
.Pada dasarnya Anda meminta kompiler untuk membiarkan Anda melakukan
TestB
operasi pada jenisTestA
yang tidak mendukungnya.sumber
Karena ini adalah pertanyaan yang banyak direferensikan, dan jawaban saat ini terutama menjelaskan mengapa itu tidak bekerja (atau mengusulkan solusi berbahaya yang tidak ingin saya lihat dalam kode produksi), saya pikir pantas menambahkan jawaban lain, menunjukkan perangkap, dan solusi yang mungkin.
Alasan mengapa ini tidak berfungsi secara umum telah ditunjukkan dalam jawaban lain: Apakah konversi benar-benar valid atau tidak tergantung pada jenis objek yang terkandung dalam daftar asli. Ketika ada objek dalam daftar yang jenisnya bukan tipe
TestB
, tetapi dari subkelas berbedaTestA
, maka gips tidak valid.Tentu saja, gips mungkin valid. Anda terkadang memiliki informasi tentang tipe-tipe yang tidak tersedia untuk kompiler. Dalam kasus ini, dimungkinkan untuk membuat daftar, tetapi secara umum, tidak disarankan :
Orang bisa ...
Implikasi dari pendekatan pertama (yang sesuai dengan jawaban yang diterima saat ini) halus. Tampaknya ini berfungsi dengan baik pada pandangan pertama. Tetapi jika ada jenis yang salah dalam daftar input, maka
ClassCastException
akan dilempar, mungkin di lokasi yang sama sekali berbeda dalam kode, dan mungkin sulit untuk men-debug ini dan mencari tahu di mana elemen yang salah masuk ke dalam daftar. Masalah terburuk adalah bahwa seseorang bahkan mungkin menambahkan elemen yang tidak valid setelah daftar telah dibuat, mempersulit proses debug.Masalah debugging palsu ini
ClassCastExceptions
dapat diatasi denganCollections#checkedCollection
keluarga metode.Memfilter daftar berdasarkan jenisnya
Cara yang lebih aman dari konversi dari a
List<Supertype>
ke aList<Subtype>
adalah dengan benar-benar memfilter daftar, dan membuat daftar baru yang hanya berisi elemen yang memiliki tipe tertentu. Ada beberapa derajat kebebasan untuk penerapan metode seperti itu (misalnya mengenai perlakuannull
entri), tetapi satu kemungkinan implementasi mungkin terlihat seperti ini:Metode ini dapat digunakan untuk memfilter daftar arbitrer (tidak hanya dengan hubungan Subtype-Supertype yang diberikan mengenai parameter tipe), seperti dalam contoh ini:
sumber
Anda tidak dapat melemparkan
List<TestB>
keList<TestA>
Steve Kuo menyebutkan TETAPI Anda dapat membuang isiList<TestA>
ke dalamList<TestB>
. Coba yang berikut ini:Saya belum mencoba kode ini sehingga mungkin ada kesalahan tetapi idenya adalah harus mengulangi objek data dengan menambahkan elemen (objek TestB) ke dalam Daftar. Saya harap itu berhasil untuk Anda.
sumber
Cara aman terbaik adalah menerapkan
AbstractList
dan melemparkan item dalam implementasi. Saya membuatListUtil
kelas pembantu:Anda dapat menggunakan
cast
metode untuk secara buta melemparkan benda-benda dalam daftar danconvert
metode untuk casting yang aman. Contoh:sumber
Satu-satunya cara saya tahu adalah dengan menyalin:
sumber
Ketika Anda melemparkan referensi objek Anda hanya casting tipe referensi, bukan tipe objek. casting tidak akan mengubah tipe objek yang sebenarnya.
Java tidak memiliki aturan implisit untuk mengonversi jenis objek. (Tidak seperti primitif)
Alih-alih, Anda perlu menyediakan cara mengonversi satu jenis ke jenis lainnya dan menyebutnya secara manual.
Ini lebih verbose daripada dalam bahasa dengan dukungan langsung, tetapi ini berfungsi dan Anda tidak perlu sering melakukannya.
sumber
jika Anda memiliki objek kelas
TestA
, Anda tidak bisa mengubahnyaTestB
. setiapTestB
adalahTestA
, tetapi tidak sebaliknya.dalam kode berikut:
baris kedua akan melempar a
ClassCastException
.Anda hanya bisa memberikan
TestA
referensi jika objek itu sendiriTestB
. sebagai contoh:jadi, Anda mungkin tidak selalu melemparkan daftar
TestA
ke daftarTestB
.sumber
Anda dapat menggunakan
selectInstances
metode ini di Eclipse Collections . Ini akan melibatkan pembuatan koleksi baru namun demikian tidak akan seefisien solusi yang diterima yang menggunakan casting.Saya termasuk
StringBuffer
dalam contoh untuk menunjukkan bahwaselectInstances
tidak hanya menurunkan tipe, tetapi juga akan memfilter jika koleksi berisi tipe campuran.Catatan: Saya pengendara untuk Eclipse Collections.
sumber
Hal ini dimungkinkan karena penghapusan tipe. Anda akan menemukannya
Secara internal kedua daftar adalah tipe
List<Object>
. Untuk alasan itu Anda tidak dapat saling melemparkan - tidak ada yang bisa dilemparkan.sumber
Integer j = 42; Integer i = (Integer) j;
berfungsi dengan baik, keduanya bilangan bulat, mereka berdua memiliki kelas yang sama, jadi "tidak ada yang dilemparkan".Masalahnya adalah bahwa metode Anda TIDAK mengembalikan daftar TestA jika mengandung TestB, jadi bagaimana jika itu diketik dengan benar? Kemudian pemain ini:
bekerja dengan baik dan Anda bisa berharap untuk (Eclipse memperingatkan Anda tentang pemain yang tidak diperiksa yang persis apa yang Anda lakukan, jadi meh). Jadi bisakah Anda menggunakan ini untuk menyelesaikan masalah Anda? Sebenarnya Anda bisa karena ini:
Tepat seperti yang Anda minta, bukan? atau lebih tepatnya:
tampaknya mengkompilasi baik untuk saya.
sumber
Tes menunjukkan ini bagaimana dilemparkan
List<Object>
keList<MyClass>
. Tetapi Anda harus memperhatikan bahwaobjectList
harus mengandung contoh dari jenis yang samaMyClass
. Dan contoh ini dapat dipertimbangkan saatList<T>
digunakan. Untuk tujuan ini, dapatkan bidangClass<T> clazz
dalam konstruktor dan gunakan sebagai gantiMyClass.class
.sumber
Ini seharusnya bekerja
sumber
Cukup aneh bahwa secara manual casting daftar masih belum disediakan oleh beberapa kotak alat yang mengimplementasikan sesuatu seperti:
Tentu saja, ini tidak akan memeriksa item satu per satu, tetapi justru itulah yang ingin kita hindari di sini, jika kita tahu bahwa implementasi kita hanya menyediakan sub-jenis.
sumber