Saya punya List<SubClass>
yang ingin saya perlakukan sebagai List<BaseClass>
. Sepertinya itu seharusnya tidak menjadi masalah karena casting SubClass
ke a BaseClass
adalah snap, tetapi kompiler saya mengeluh bahwa para pemain tidak mungkin.
Jadi, apa cara terbaik untuk mendapatkan referensi ke objek yang sama dengan List<BaseClass>
?
Saat ini saya hanya membuat daftar baru dan menyalin daftar lama:
List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)
Tapi seperti yang saya pahami itu harus membuat daftar yang sama sekali baru. Saya ingin referensi ke daftar asli, jika memungkinkan!
java
inheritance
casting
Riley Lark
sumber
sumber
Jawaban:
Sintaks untuk jenis tugas ini menggunakan wildcard:
Sangat penting untuk menyadari bahwa
List<SubClass>
ini tidak dipertukarkan denganList<BaseClass>
. Kode yang mempertahankan referensi keList<SubClass>
akan mengharapkan setiap item dalam daftar menjadi aSubClass
. Jika bagian lain dari kode disebut daftar sebagaiList<BaseClass>
, kompiler tidak akan mengeluh ketika aBaseClass
atauAnotherSubClass
dimasukkan. Tetapi ini akan menyebabkan aClassCastException
untuk potongan kode pertama, yang mengasumsikan bahwa semua yang ada dalam daftar adalah aSubClass
.Koleksi generik tidak berperilaku sama dengan array di Jawa. Array adalah kovarian; yaitu, diizinkan untuk melakukan ini:
Ini diperbolehkan, karena array "tahu" jenis elemen-elemennya. Jika seseorang mencoba untuk menyimpan sesuatu yang bukan turunan dari
SubClass
array (melaluibases
referensi), pengecualian runtime akan dibuang.Koleksi generik tidak "tahu" jenis komponennya; informasi ini "dihapus" pada waktu kompilasi. Oleh karena itu, mereka tidak dapat menaikkan pengecualian runtime ketika toko yang tidak valid terjadi. Alih-alih, a
ClassCastException
akan dimunculkan pada titik kode yang jauh dan sulit diasosiasikan ketika nilai dibaca dari koleksi. Jika Anda memperhatikan peringatan kompiler tentang keamanan tipe, Anda akan menghindari kesalahan tipe ini saat runtime.sumber
erickson sudah menjelaskan mengapa Anda tidak bisa melakukan ini, tetapi di sini ada beberapa solusi:
Jika Anda hanya ingin mengambil unsur-unsur dari daftar dasar Anda, pada prinsipnya metode penerima Anda harus dinyatakan sebagai mengambil
List<? extends BaseClass>
.Tetapi jika tidak dan Anda tidak dapat mengubahnya, Anda dapat membungkus daftar dengan
Collections.unmodifiableList(...)
, yang memungkinkan mengembalikan Daftar supertype dari parameter argumen. (Ini menghindari masalah keamanan jenis dengan melempar UnsupportedOperationException pada percobaan penyisipan.)sumber
Seperti yang dijelaskan oleh @erickson, jika Anda benar-benar ingin referensi ke daftar asli, pastikan tidak ada kode yang menyisipkan apa pun ke daftar itu jika Anda ingin menggunakannya lagi di bawah deklarasi aslinya. Cara termudah untuk mendapatkannya adalah dengan hanya membuangnya ke daftar ungenerik lama:
Saya tidak akan merekomendasikan ini jika Anda tidak tahu apa yang terjadi pada Daftar dan akan menyarankan Anda mengubah kode apa pun yang perlu Daftar untuk menerima Daftar yang Anda miliki.
sumber
Di bawah ini adalah cuplikan berguna yang berfungsi. Itu membangun daftar array baru tetapi pembuatan objek JVM di atas kepala tidak signifikan.
Saya melihat jawaban lain tidak perlu rumit.
sumber
Saya melewatkan jawaban di mana Anda baru saja melemparkan daftar asli, menggunakan gips ganda. Jadi ini untuk kelengkapan:
Tidak ada yang disalin, dan operasinya cepat. Namun, Anda menipu kompilator di sini sehingga Anda harus benar-benar memastikan untuk tidak mengubah daftar sedemikian rupa sehingga
subList
mulai berisi item dari sub jenis yang berbeda. Ketika berhadapan dengan daftar abadi, ini biasanya bukan masalah.sumber
List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)
sumber
Apa yang Anda coba lakukan sangat berguna dan saya menemukan bahwa saya perlu melakukannya sangat sering dalam kode yang saya tulis. Contoh use case:
Katakanlah kami memiliki antarmuka
Foo
dan kami memilikizorking
paket yang memilikiZorkingFooManager
yang membuat dan mengelola instance dari paket-pribadiZorkingFoo implements Foo
. (Skenario yang sangat umum.)Jadi,
ZorkingFooManager
perlu mengandungprivate Collection<ZorkingFoo> zorkingFoos
tetapi perlu mengekspos apublic Collection<Foo> getAllFoos()
.Sebagian besar programmer java tidak akan berpikir dua kali sebelum mengimplementasikan
getAllFoos()
sebagai mengalokasikan yang baruArrayList<Foo>
, mengisinya dengan semua elemen darizorkingFoos
dan mengembalikannya. Saya menikmati menghibur pikiran bahwa sekitar 30% dari semua siklus jam yang dikonsumsi oleh kode java yang berjalan pada jutaan mesin di seluruh planet ini tidak melakukan apa pun selain membuat salinan ArrayLists yang tidak berguna yang merupakan mikrodetik sampah yang dikumpulkan setelah pembuatannya.Solusi untuk masalah ini, tentu saja, menurunkan koleksi. Inilah cara terbaik untuk melakukannya:
Yang membawa kita ke
castList()
fungsi:result
Variabel perantara diperlukan karena penyimpangan bahasa java:return (List<T>)list;
menghasilkan pengecualian "pemain yang tidak dicentang"; sejauh ini baik; tapi kemudian:@SuppressWarnings( "unchecked" ) return (List<T>)list;
adalah penggunaan anotasi penindasan-peringatan secara ilegal.Jadi, meskipun tidak halal untuk digunakan
@SuppressWarnings
padareturn
pernyataan, tampaknya baik-baik saja untuk menggunakannya pada tugas, sehingga variabel "hasil" tambahan menyelesaikan masalah ini. (Itu harus dioptimalkan baik oleh kompiler atau oleh JIT.)sumber
Sesuatu seperti ini seharusnya bekerja juga:
Tidak benar-benar tahu mengapa clazz diperlukan di Eclipse ..
sumber
Bagaimana dengan casting semua elemen. Ini akan membuat daftar baru, tetapi akan merujuk objek asli dari daftar lama.
sumber
Ini adalah bagian kode yang lengkap menggunakan Generics, untuk memberikan daftar sub kelas ke kelas super.
Metode penelepon yang melewati tipe subclass
Metode Callee yang menerima subtipe dari kelas dasar
sumber