Eclipse memberi saya peringatan tentang bentuk berikut:
Jenis keamanan: Pemain yang tidak dicentang dari Object ke HashMap
Ini dari panggilan ke API yang saya tidak punya kendali atas yang mengembalikan Objek:
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
Saya ingin menghindari peringatan Eclipse, jika mungkin, karena secara teoritis mereka menunjukkan setidaknya masalah kode potensial. Saya belum menemukan cara yang baik untuk menghilangkan yang satu ini. Saya dapat mengekstrak satu baris yang terlibat ke metode dengan sendirinya dan menambahkan @SuppressWarnings("unchecked")
ke metode itu, sehingga membatasi dampak memiliki blok kode di mana saya mengabaikan peringatan. Ada opsi yang lebih baik? Saya tidak ingin mematikan peringatan ini di Eclipse.
Sebelum saya sampai pada kode, itu lebih sederhana, tetapi masih memancing peringatan:
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
Masalah ada di tempat lain saat Anda mencoba menggunakan hash, Anda akan mendapat peringatan:
HashMap items = getItems(session);
items.put("this", "that");
Type safety: The method put(Object, Object) belongs to the raw type HashMap. References to generic type HashMap<K,V> should be parameterized.
enum
atau bahkan contohClass<T>
), sehingga Anda dapat meliriknya dan mengetahui bahwa itu aman.Jawaban:
Jawaban yang jelas, tentu saja, adalah tidak melakukan pemeran yang tidak diperiksa.
Jika benar-benar diperlukan, maka setidaknya cobalah untuk membatasi ruang lingkup
@SuppressWarnings
anotasi. Menurut Javadocs - nya , ia bisa menggunakan variabel lokal; dengan cara ini, itu bahkan tidak mempengaruhi keseluruhan metode.Contoh:
Tidak ada cara untuk menentukan apakah
Map
benar - benar harus memiliki parameter generik<String, String>
. Anda harus tahu sebelumnya apa yang seharusnya menjadi parameter (atau Anda akan mengetahui kapan Anda mendapatkanClassCastException
). Inilah sebabnya kode menghasilkan peringatan, karena kompilator tidak mungkin tahu apakah aman.sumber
String s = (String) new Object() ;
tidak mendapat peringatan, meskipun kompiler tidak tahu bahwa gips aman. Peringatan itu karena kompiler (a) tidak tahu bahwa para pemain aman DAN (b) tidak akan menghasilkan pemeriksaan run-time lengkap pada titik para pemain. Akan ada cek bahwa itu adalahHashmap
, tetapi tidak akan ada cek bahwa itu adalahHashMap<String,String>
.Sayangnya, tidak ada opsi bagus di sini. Ingat, tujuan semua ini adalah untuk menjaga keamanan tipe. " Java Generics " menawarkan solusi untuk berurusan dengan perpustakaan warisan non-generik, dan ada satu yang secara khusus disebut "teknik loop kosong" di bagian 8.2. Pada dasarnya, buat pemain yang tidak aman, dan tahan peringatan. Kemudian putar melalui peta seperti ini:
Jika jenis yang tidak terduga ditemukan, Anda akan mendapatkan runtime
ClassCastException
, tetapi setidaknya itu akan terjadi dekat dengan sumber masalah.sumber
Wow; Saya pikir saya menemukan jawaban untuk pertanyaan saya sendiri. Aku hanya tidak yakin itu layak! :)
Masalahnya adalah para pemain tidak diperiksa. Jadi, Anda harus memeriksanya sendiri. Anda tidak bisa hanya memeriksa tipe parameter dengan instanceof, karena informasi tipe parameter tidak tersedia saat runtime, telah dihapus pada waktu kompilasi.
Tapi, Anda bisa melakukan pemeriksaan pada setiap item di hash, dengan instanceof, dan dengan melakukan itu, Anda bisa membuat hash baru yang aman untuk mengetik. Dan Anda tidak akan memprovokasi peringatan apa pun.
Berkat mmyers dan Esko Luontola, saya telah membuat parameter kode yang awalnya saya tulis di sini, sehingga dapat dibungkus dalam kelas utilitas di suatu tempat dan digunakan untuk HashMap yang diparameterisasi. Jika Anda ingin memahaminya lebih baik dan tidak terlalu akrab dengan obat generik, saya sarankan untuk melihat riwayat edit dari jawaban ini.
Itu banyak pekerjaan, mungkin untuk hadiah yang sangat sedikit ... Saya tidak yakin apakah saya akan menggunakannya atau tidak. Saya menghargai setiap komentar apakah orang berpikir itu layak atau tidak. Juga, saya menghargai saran peningkatan: apakah ada sesuatu yang lebih baik yang bisa saya lakukan selain melempar AssertionErrors? Apakah ada sesuatu yang lebih baik yang bisa saya lempar? Haruskah saya menjadikannya Pengecualian yang dicentang?
sumber
Dalam Eclipse Preferences, Pergi ke Java-> Compiler-> Kesalahan / Peringatan-> Jenis generik dan centang kotak
Ignore unavoidable generic type problems
centang.Ini memenuhi maksud pertanyaan, yaitu
kalau bukan semangat.
sumber
uses unchecked or unsafe operations.
" kesalahanjavac
, tetapi menambahkan@SuppressWarnings("unchecked")
membuat Eclipse tidak senang, mengklaim penindasan itu tidak perlu. Hapus centang pada kotak ini membuat Eclipse danjavac
berperilaku sama, itulah yang saya inginkan. Menekan secara eksplisit peringatan dalam kode jauh lebih jelas daripada menekannya di mana pun di dalam Eclipse.Anda dapat membuat kelas utilitas seperti berikut ini, dan menggunakannya untuk menekan peringatan yang tidak dicentang.
Anda dapat menggunakannya sebagai berikut:
Beberapa diskusi lebih lanjut tentang ini ada di sini: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
sumber
vi
? Apa Anda sedang bercanda?Hal ini sulit, tetapi inilah pemikiran saya saat ini:
Jika API Anda mengembalikan Objek, maka tidak ada yang dapat Anda lakukan - apa pun yang terjadi, Anda akan secara buta melempar objek tersebut. Anda membiarkan Java melempar ClassCastExceptions, atau Anda dapat memeriksa setiap elemen sendiri dan melemparkan Assertions atau IllegalArgumentExceptions atau semacamnya, tetapi pemeriksaan runtime ini semuanya setara. Anda harus menekan waktu kompilasi yang tidak dicentang, tidak peduli apa yang Anda lakukan saat runtime.
Saya hanya lebih suka blind cast dan membiarkan JVM melakukan pemeriksaan runtime untuk saya karena kita "tahu" apa yang harus dikembalikan oleh API, dan biasanya bersedia untuk menganggap bahwa API berfungsi. Gunakan obat generik di mana-mana di atas gips, jika Anda membutuhkannya. Anda tidak benar-benar membeli apa pun di sana karena Anda masih memiliki single blind cast, tetapi setidaknya Anda dapat menggunakan obat generik dari sana sehingga JVM dapat membantu Anda menghindari cast buta di bagian lain dari kode Anda.
Dalam kasus khusus ini, mungkin Anda dapat melihat panggilan ke SetAttribute dan melihat jenisnya masuk, jadi cukup tutupi jenis yang sama agar jalan keluarnya tidak bermoral. Tambahkan komentar yang mereferensikan SetAttribute dan selesaikanlah.
sumber
Berikut adalah contoh singkat yang menghindari peringatan "pemeran yang tidak diperiksa" dengan menggunakan dua strategi yang disebutkan dalam jawaban lain.
Lewatkan Kelas jenis minat sebagai parameter saat runtime (
Class<T> inputElementClazz
). Maka Anda dapat menggunakan:inputElementClazz.cast(anyObject);
Untuk jenis casting Koleksi, gunakan wildcard? bukannya tipe T generik untuk mengakui bahwa Anda memang tidak tahu objek seperti apa yang diharapkan dari kode lama (
Collection<?> unknownTypeCollection
). Lagi pula, inilah yang ingin disampaikan oleh peringatan "pemain yang tidak diperiksa": Kami tidak dapat memastikan bahwa kami mendapatkanCollection<T>
, jadi hal yang jujur untuk dilakukan adalah menggunakan aCollection<?>
. Jika benar-benar diperlukan, koleksi tipe yang dikenal masih dapat dibangun (Collection<T> knownTypeCollection
).Kode lama yang dihubungkan pada contoh di bawah ini memiliki atribut "input" di StructuredViewer (StructuredViewer adalah widget pohon atau tabel, "input" adalah model data di belakangnya). "Input" ini bisa berupa Java Collection apa saja.
Secara alami, kode di atas dapat memberikan kesalahan runtime jika kita menggunakan kode lama dengan tipe data yang salah (misalnya jika kita menetapkan array sebagai "input" dari StructuredViewer alih-alih Koleksi Java).
Contoh memanggil metode:
sumber
Di dunia HTTP Session Anda tidak dapat benar-benar menghindari para pemeran, karena API ditulis seperti itu (hanya mengambil dan mengembalikan
Object
).Dengan sedikit kerja, Anda dapat dengan mudah menghindari para pemain yang tidak dicentang, 'meskipun. Ini berarti bahwa itu akan berubah menjadi pemain tradisional memberi
ClassCastException
hak di sana jika terjadi kesalahan). Pengecualian yang tidak dicentang bisa berubah menjadiCCE
pada titik mana saja di kemudian hari alih-alih titik para pemeran (itulah alasan mengapa ini merupakan peringatan terpisah).Ganti HashMap dengan kelas khusus:
Kemudian dilemparkan ke kelas itu dan bukannya
Map<String,String>
dan semuanya akan diperiksa di tempat yang tepat di mana Anda menulis kode Anda. Tidak ada yang tak terdugaClassCastExceptions
nantinya.sumber
Di Android Studio jika Anda ingin menonaktifkan inspeksi, Anda dapat menggunakan:
sumber
Dalam kasus khusus ini, saya tidak akan menyimpan Maps ke dalam HttpSession secara langsung, melainkan sebuah instance dari kelas saya sendiri, yang pada gilirannya berisi Peta (detail implementasi dari kelas). Kemudian Anda dapat yakin bahwa elemen-elemen dalam peta adalah jenis yang tepat.
Tetapi jika Anda ingin mengecek bahwa isi Peta adalah tipe yang benar, Anda dapat menggunakan kode seperti ini:
sumber
Objek. Fungsi utilitas yang tidak diperiksa dalam jawaban di atas oleh Esko Luontola adalah cara yang bagus untuk menghindari kekacauan program.
Jika Anda tidak ingin SuppressWarnings di seluruh metode, Java memaksa Anda untuk meletakkannya di lokal. Jika Anda membutuhkan gips pada anggota itu dapat menyebabkan kode seperti ini:
Menggunakan utilitas jauh lebih bersih, dan masih jelas apa yang Anda lakukan:
CATATAN: Saya merasa penting untuk menambahkan bahwa terkadang peringatan itu benar-benar berarti Anda melakukan sesuatu yang salah seperti:
Apa yang dikatakan kompiler kepada Anda adalah bahwa pemeran ini TIDAK akan diperiksa saat runtime, jadi tidak ada kesalahan runtime yang akan dinaikkan hingga Anda mencoba mengakses data dalam wadah umum.
sumber
Penindasan peringatan bukanlah solusi. Anda seharusnya tidak melakukan casting dua level dalam satu pernyataan.
sumber
Tebakan cepat jika Anda memposting kode Anda dapat mengatakan dengan pasti tetapi Anda mungkin telah melakukan sesuatu di sepanjang baris
yang akan menghasilkan peringatan saat Anda perlu melakukannya
mungkin layak untuk dilihat
Generik dalam Bahasa Pemrograman Java
jika Anda tidak terbiasa dengan apa yang perlu dilakukan.
sumber
Saya mungkin salah paham pertanyaannya (contoh dan beberapa baris di sekitarnya akan lebih baik), tetapi mengapa Anda tidak selalu menggunakan antarmuka yang sesuai (dan Java5 +)? Saya tidak melihat alasan mengapa Anda ingin menggunakan yang
HashMap
bukanMap<KeyType,ValueType>
. Bahkan, saya tidak bisa membayangkan apa alasan untuk mengatur jenis variabel untukHashMap
bukanMap
.Dan mengapa sumbernya adalah
Object
? Apakah ini tipe parameter koleksi lawas? Jika demikian, gunakan obat generik dan tentukan jenis yang Anda inginkan.sumber
Jika saya harus menggunakan API yang tidak mendukung Generics .. Saya mencoba dan mengisolasi panggilan-panggilan itu dalam rutinitas wrapper dengan garis sesedikit mungkin. Saya kemudian menggunakan anotasi SuppressWarnings dan juga menambahkan gips keselamatan-jenis pada saat yang sama.
Ini hanya preferensi pribadi untuk menjaga hal-hal serapi mungkin.
sumber
Ambil yang ini, ini jauh lebih cepat daripada membuat HashMap baru, jika sudah satu, tapi masih aman, karena setiap elemen diperiksa terhadap tipenya ...
sumber
key.isAssignableFrom(e.getKey().getClass())
dapat ditulis sebagaikey.isInstance(e.getKey())
Cukup ketik centang sebelum Anda melemparkannya.
Dan bagi siapa pun yang bertanya, sangat umum untuk menerima objek di mana Anda tidak yakin dengan tipenya. Banyak implementasi "SOA" warisan melewati berbagai objek yang tidak selalu harus Anda percayai. (Kengerian!)
EDIT Mengubah kode contoh satu kali untuk mencocokkan pembaruan poster, dan mengikuti beberapa komentar, saya melihat bahwa instanceof tidak cocok dengan obat generik. Namun mengubah centang untuk memvalidasi objek luar tampaknya cocok dengan kompiler baris perintah. Contoh yang direvisi sekarang diposting.
sumber
Hampir setiap masalah dalam Ilmu Komputer dapat diselesaikan dengan menambahkan tingkat tipuan *, atau sesuatu.
Jadi perkenalkan objek non-generik yang memiliki level lebih tinggi dari a
Map
. Tanpa konteks itu tidak akan terlihat sangat meyakinkan, tapi bagaimanapun:* Kecuali terlalu banyak level tipuan.
sumber
Inilah satu cara saya menangani ini ketika saya menimpa
equals()
operasi.Ini tampaknya berfungsi di Java 8 (bahkan dikompilasi dengan
-Xlint:unchecked
)sumber
Jika Anda yakin bahwa tipe yang dikembalikan oleh session.getAttribute () adalah HashMap maka Anda tidak dapat mengetikkan typecast ke tipe yang tepat, tetapi hanya mengandalkan memeriksa HashMap generik
Eclipse kemudian akan mengejutkan peringatan, tetapi tentu saja ini dapat menyebabkan kesalahan runtime yang bisa sulit untuk di-debug. Saya menggunakan pendekatan ini dalam konteks tidak hanya operasi kritis.
sumber
Dua cara, satu yang menghindari tag sepenuhnya, yang lain menggunakan metode utilitas nakal tapi bagus.
Masalahnya adalah Koleksi pra-generik ...
Saya percaya aturan praktisnya adalah: "melemparkan objek satu hal pada satu waktu" - apa artinya ini ketika mencoba menggunakan kelas mentah di dunia generik adalah karena Anda tidak tahu apa ada di Peta ini <?,?> (dan memang JVM bahkan mungkin menemukan bahwa itu bahkan bukan Peta!), itu jelas ketika Anda berpikir tentang hal itu bahwa Anda tidak dapat melemparkannya. Jika Anda memiliki Map <String,?> Map2 maka HashSet <String> keys = (HashSet <String>) map2.keySet () tidak memberi Anda peringatan, meskipun ini merupakan "tindakan iman" untuk kompiler (karena mungkin berubah menjadi TreeSet) ... tetapi itu hanya satu tindakan iman.
PS dengan keberatan bahwa iterasi seperti dalam cara pertama saya "membosankan" dan "membutuhkan waktu", jawabannya adalah "tidak sakit tanpa untung": koleksi umum dijamin mengandung Map.Entry <String, String> s, dan tidak ada lain. Anda harus membayar untuk jaminan ini. Saat menggunakan obat generik secara sistematis, pembayaran ini, dengan indahnya, berupa kepatuhan pada kode, bukan waktu mesin!
Satu aliran pemikiran mungkin mengatakan bahwa Anda harus mengatur pengaturan Eclipse untuk membuat kesalahan pemeran yang tidak dicentang, alih-alih peringatan. Dalam hal ini Anda harus menggunakan cara pertama saya.
sumber
Ini membuat peringatan hilang ...
sumber
Solusi: Nonaktifkan peringatan ini di Eclipse. Jangan @SuppressWarnings itu, cukup nonaktifkan saja.
Beberapa "solusi" yang disajikan di atas adalah jalan keluar dari jalur, membuat kode tidak dapat dibaca demi menekan peringatan konyol.
sumber
@SuppressWarnings
tidak membuat kode tidak dapat dibaca sama sekali.