Bagaimana cara mengatasi peringatan pemeran yang tidak dicentang?

611

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.
skiphoppy
sumber
Jika Anda menggunakan HttpSession seperti itu, lihat artikel Brian Goetz pada subjek: ibm.com/developerworks/library/j-jtp09238.html
Tom Hawtin - tackline
Jika pemain yang tidak diperiksa tidak dapat dihindari, ide yang bagus adalah memasangkannya dengan sesuatu yang secara logis mewakili jenisnya (seperti contoh enumatau bahkan contoh Class<T>), sehingga Anda dapat meliriknya dan mengetahui bahwa itu aman.
Philip Guin
4
Terkait / dupe: Jenis keamanan: Pemain tidak dicentang
blahdiblah
3
kemungkinan duplikat keselamatan Tipe: Pemain yang tidak dicentang
Raedwald
Saya akan menambahkan, saya menemukan saya hanya bisa menambahkan @SuppressWarnings ("tidak dicentang") pada tingkat metode yang berisi kode yang menyinggung. Jadi saya memecahkan kode ke rutin di mana saya harus melakukan ini. Saya selalu berpikir Anda bisa melakukan ini segera di atas garis yang dimaksud.
JGFMK

Jawaban:

557

Jawaban yang jelas, tentu saja, adalah tidak melakukan pemeran yang tidak diperiksa.

Jika benar-benar diperlukan, maka setidaknya cobalah untuk membatasi ruang lingkup @SuppressWarningsanotasi. Menurut Javadocs - nya , ia bisa menggunakan variabel lokal; dengan cara ini, itu bahkan tidak mempengaruhi keseluruhan metode.

Contoh:

@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();

Tidak ada cara untuk menentukan apakah Mapbenar - benar harus memiliki parameter generik <String, String>. Anda harus tahu sebelumnya apa yang seharusnya menjadi parameter (atau Anda akan mengetahui kapan Anda mendapatkan ClassCastException). Inilah sebabnya kode menghasilkan peringatan, karena kompilator tidak mungkin tahu apakah aman.

Michael Myers
sumber
112
+1 untuk menunjukkan bahwa ini bisa menggunakan variabel lokal. Eclipse hanya menawarkan untuk menambahkannya ke seluruh metode ...
thSoft
17
Eclipse 3.7 (Indigo) memiliki dukungan untuk menambahkan yang tidak dicentang ke variabel lokal.
sweetfa
78
Peringatan itu bukan hanya karena kompiler tidak tahu bahwa para pemain aman. Misalnya 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 adalah Hashmap, tetapi tidak akan ada cek bahwa itu adalah HashMap<String,String>.
Theodore Norvell
9
Sedihnya, meskipun para pemeran dan peringatan adalah untuk tugas , anotasi harus pergi pada deklarasi variabel ... Jadi jika deklarasi dan tugas berada di tempat yang berbeda (katakanlah, masing-masing di luar dan di dalam blok 'coba') , Eclipse sekarang menghasilkan dua peringatan: pemeran asli yang tidak dicentang, dan diagnostik "anotasi yang tidak perlu" yang baru.
Ti Strga
6
Solusi untuk penjelasan yang perlu menyertai deklarasi variabel lokal, yang mungkin berada dalam ruang lingkup yang berbeda pada garis yang berbeda dari gips yang sebenarnya, adalah membuat variabel lokal dalam ruang lingkup gips khusus untuk melakukan gips pada baris yang sama sebagai deklarasi. Kemudian tetapkan variabel ini ke variabel aktual yang berada dalam cakupan yang berbeda. Ini adalah metode yang saya gunakan juga untuk menekan peringatan pada gips ke variabel instan karena penjelasannya juga tidak dapat diterapkan di sini.
Jeff Lockhart
169

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:

@SuppressWarnings("unchecked")
Map<String, Number> map = getMap();
for (String s : map.keySet());
for (Number n : map.values());

Jika jenis yang tidak terduga ditemukan, Anda akan mendapatkan runtime ClassCastException, tetapi setidaknya itu akan terjadi dekat dengan sumber masalah.

Julien Chastang
sumber
6
Jawaban yang jauh lebih baik daripada yang disediakan oleh skiphoppy, karena beberapa alasan: 1) Kode ini jauh, jauh lebih pendek. 2) Kode ini sebenarnya melempar ClassCastException seperti yang diharapkan. 3) Kode ini tidak melakukan salinan lengkap dari peta sumber. 4) Loop dapat dengan mudah dibungkus dengan metode terpisah yang digunakan dalam pernyataan, yang akan dengan mudah menghapus hit kinerja dalam kode produksi.
Stijn de Witt
6
Tidakkah ada kemungkinan bahwa kompiler Java atau kompiler JIT akan memutuskan bahwa hasil dari kode ini tidak digunakan dan "mengoptimalkannya" dengan tidak mengompilasinya?
RenniePet
1
Ini bukan kode yang benar-benar mati jika berpotensi membuang pengecualian. Saya tidak cukup tahu tentang kompiler JIT yang digunakan hari ini untuk menjamin bahwa tidak ada dari mereka yang akan mengacaukan ini, tetapi saya merasa cukup percaya diri dalam mengatakan bahwa mereka tidak seharusnya melakukannya.
GrandOpener
3
Ini masih tidak menjamin keamanan jenis karena peta yang sama masih digunakan. Awalnya mungkin telah didefinisikan sebagai Peta <Objek, Objek> yang kebetulan memiliki String dan Angka di kemudian hari jika Boolean ditambahkan maka pengguna kode ini akan mendapatkan kejutan yang membingungkan dan agak sulit dilacak. Satu-satunya cara untuk menjamin keamanan tipe adalah menyalinnya ke peta baru dengan tipe yang diminta yang menjamin apa yang diizinkan masuk ke dalamnya.
user2219808
112

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.

public static <K, V> HashMap<K, V> castHash(HashMap input,
                                            Class<K> keyClass,
                                            Class<V> valueClass) {
  HashMap<K, V> output = new HashMap<K, V>();
  if (input == null)
      return output;
  for (Object key: input.keySet().toArray()) {
    if ((key == null) || (keyClass.isAssignableFrom(key.getClass()))) {
        Object value = input.get(key);
        if ((value == null) || (valueClass.isAssignableFrom(value.getClass()))) {
            K k = keyClass.cast(key);
            V v = valueClass.cast(value);
            output.put(k, v);
        } else {
            throw new AssertionError(
                "Cannot cast to HashMap<"+ keyClass.getSimpleName()
                +", "+ valueClass.getSimpleName() +">"
                +", value "+ value +" is not a "+ valueClass.getSimpleName()
            );
        }
    } else {
        throw new AssertionError(
            "Cannot cast to HashMap<"+ keyClass.getSimpleName()
            +", "+ valueClass.getSimpleName() +">"
            +", key "+ key +" is not a " + keyClass.getSimpleName()
        );
    }
  }
  return output;
}

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?

skiphoppy
sumber
68
hal ini membingungkan, tetapi saya pikir semua yang Anda lakukan adalah diperdagangkan ClassCastExceptions untuk AssertionErrors.
Dustin Getz
59
Bung, itu pasti tidak sepadan! Bayangkan getah miskin yang harus kembali dan memodifikasi beberapa kode dengan kekacauan di sana. Saya tidak suka menekan peringatan, tapi saya pikir itu adalah kejahatan yang lebih kecil di sini.
Craig B
69
Bukan hanya itu yang berantakan, membingungkan, berantakan (ketika Anda tidak dapat menghindari satu komentar berlebihan dapat berjalan programmer pemeliharaan melaluinya); iterating atas setiap elemen dalam koleksi mengubah gips dari operasi O (1) ke O (n). Ini adalah sesuatu yang tidak pernah diharapkan dan dapat dengan mudah berubah menjadi perlambatan misteri yang mengerikan.
Dan Is Fiddling By Firelight
22
@ Dan kamu benar. Secara umum, tidak ada yang seharusnya melakukan ini.
skiphoppy
4
Beberapa komentar ... tanda tangan metode salah karena tidak "membuang" apa-apa, itu hanya menyalin peta yang ada ke peta baru. Selain itu, mungkin bisa dire-reforedored untuk menerima peta apa pun, dan tidak bergantung pada HashMap itu sendiri (yaitu mengambil Peta dan mengembalikan Peta dalam tanda tangan metode, bahkan jika tipe internal adalah HashMap). Anda tidak benar-benar perlu melakukan casting atau penyimpanan ke peta baru - jika Anda tidak melempar kesalahan pernyataan, maka peta yang diberikan memiliki jenis yang tepat di dalamnya sampai sekarang. Membuat peta baru dengan tipe generik tidak ada gunanya karena Anda masih bisa membuatnya mentah dan meletakkan apa pun.
MetroidFan2002
51

Dalam Eclipse Preferences, Pergi ke Java-> Compiler-> Kesalahan / Peringatan-> Jenis generik dan centang kotak Ignore unavoidable generic type problemscentang.

Ini memenuhi maksud pertanyaan, yaitu

Saya ingin menghindari peringatan Eclipse ...

kalau bukan semangat.

Dave
sumber
1
Ah, terima kasih untuk ini :) Saya mendapat " uses unchecked or unsafe operations." kesalahan javac, tetapi menambahkan @SuppressWarnings("unchecked")membuat Eclipse tidak senang, mengklaim penindasan itu tidak perlu. Hapus centang pada kotak ini membuat Eclipse dan javacberperilaku sama, itulah yang saya inginkan. Menekan secara eksplisit peringatan dalam kode jauh lebih jelas daripada menekannya di mana pun di dalam Eclipse.
dimo414
26

Anda dapat membuat kelas utilitas seperti berikut ini, dan menggunakannya untuk menekan peringatan yang tidak dicentang.

public class Objects {

    /**
     * Helps to avoid using {@code @SuppressWarnings({"unchecked"})} when casting to a generic type.
     */
    @SuppressWarnings({"unchecked"})
    public static <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }
}

Anda dapat menggunakannya sebagai berikut:

import static Objects.uncheckedCast;
...

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
      return uncheckedCast(session.getAttribute("attributeKey"));
}

Beberapa diskusi lebih lanjut tentang ini ada di sini: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html

Esko Luontola
sumber
18
tidak downvoting, tetapi bungkusnya justru menambahkan tidak lebih dari hanya menekan peringatan.
Dustin Getz
3
+1 karena solusi ini tidak menyia-nyiakan baris kode berharga
Tino
1
@ErikE Terlalu banyak. Jauh lebih mahal monitor yang lebih besar dan resolusi lebih tinggi untuk memberikan ruang bagi semua saluran yang terbuang, meja yang lebih besar untuk meletakkan semua monitor yang lebih besar, ruang yang lebih besar untuk meletakkan meja yang lebih besar, dan bos yang berwawasan luas ..
Tino
1
@ErikE Scrollbars, untuk vi? Apa Anda sedang bercanda?
Tino
21

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.

Dustin Getz
sumber
16

Berikut adalah contoh singkat yang menghindari peringatan "pemeran yang tidak diperiksa" dengan menggunakan dua strategi yang disebutkan dalam jawaban lain.

  1. Lewatkan Kelas jenis minat sebagai parameter saat runtime ( Class<T> inputElementClazz). Maka Anda dapat menggunakan:inputElementClazz.cast(anyObject);

  2. 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 mendapatkan Collection<T>, jadi hal yang jujur ​​untuk dilakukan adalah menggunakan a Collection<?>. 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.

public void dragFinished(StructuredViewer structuredViewer, Class<T> inputElementClazz) {
    IStructuredSelection selection = (IStructuredSelection) structuredViewer.getSelection();
    // legacy code returns an Object from getFirstElement,
    // the developer knows/hopes it is of type inputElementClazz, but the compiler cannot know
    T firstElement = inputElementClazz.cast(selection.getFirstElement());

    // legacy code returns an object from getInput, so we deal with it as a Collection<?>
    Collection<?> unknownTypeCollection = (Collection<?>) structuredViewer.getInput();

    // for some operations we do not even need a collection with known types
    unknownTypeCollection.remove(firstElement);

    // nothing prevents us from building a Collection of a known type, should we really need one
    Collection<T> knownTypeCollection = new ArrayList<T>();
    for (Object object : unknownTypeCollection) {
        T aT = inputElementClazz.cast(object);
        knownTypeCollection.add(aT);
        System.out.println(aT.getClass());
    }

    structuredViewer.refresh();
}

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:

dragFinishedStrategy.dragFinished(viewer, Product.class);
StaticNoiseLog
sumber
13

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 ClassCastExceptionhak di sana jika terjadi kesalahan). Pengecualian yang tidak dicentang bisa berubah menjadi CCEpada titik mana saja di kemudian hari alih-alih titik para pemeran (itulah alasan mengapa ini merupakan peringatan terpisah).

Ganti HashMap dengan kelas khusus:

import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Attributes extends AbstractMap<String, String> {
    final Map<String, String> content = new HashMap<String, String>();

    @Override
    public Set<Map.Entry<String, String>> entrySet() {
        return content.entrySet();
    }

    @Override
    public Set<String> keySet() {
        return content.keySet();
    }

    @Override
    public Collection<String> values() {
        return content.values();
    }

    @Override
    public String put(final String key, final String value) {
        return content.put(key, value);
    }
}

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 terduga ClassCastExceptionsnantinya.

Joachim Sauer
sumber
Ini jawaban yang sangat membantu.
GPrathap
10

Di Android Studio jika Anda ingin menonaktifkan inspeksi, Anda dapat menggunakan:

//noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();
Jan Moravec
sumber
2
Ini bekerja di IntelliJ IDE juga
neXus
8

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:

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("a", 1);
    map.put("b", 2);
    Object obj = map;

    Map<String, Integer> ok = safeCastMap(obj, String.class, Integer.class);
    Map<String, String> error = safeCastMap(obj, String.class, String.class);
}

@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> safeCastMap(Object map, Class<K> keyType, Class<V> valueType) {
    checkMap(map);
    checkMapContents(keyType, valueType, (Map<?, ?>) map);
    return (Map<K, V>) map;
}

private static void checkMap(Object map) {
    checkType(Map.class, map);
}

private static <K, V> void checkMapContents(Class<K> keyType, Class<V> valueType, Map<?, ?> map) {
    for (Map.Entry<?, ?> entry : map.entrySet()) {
        checkType(keyType, entry.getKey());
        checkType(valueType, entry.getValue());
    }
}

private static <K> void checkType(Class<K> expectedType, Object obj) {
    if (!expectedType.isInstance(obj)) {
        throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj);
    }
}
Esko Luontola
sumber
1
Luar biasa; Saya pikir saya bisa menggabungkannya dengan jawaban saya untuk membuat parameter dan menghindari keharusan menekan peringatan sama sekali!
skiphoppy
1
+1 mungkin resep terbaik (mudah dimengerti dan dipelihara) untuk melakukannya dengan aman dengan pemeriksaan runtime
Tino
8

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:

@SuppressWarnings("unchecked")
Vector<String> watchedSymbolsClone = (Vector<String>) watchedSymbols.clone();
this.watchedSymbols = watchedSymbolsClone;

Menggunakan utilitas jauh lebih bersih, dan masih jelas apa yang Anda lakukan:

this.watchedSymbols = Objects.uncheckedCast(watchedSymbols.clone());

CATATAN: Saya merasa penting untuk menambahkan bahwa terkadang peringatan itu benar-benar berarti Anda melakukan sesuatu yang salah seperti:

ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(1);
Object intListObject = intList; 

 // this line gives an unchecked warning - but no runtime error
ArrayList<String> stringList  = (ArrayList<String>) intListObject;
System.out.println(stringList.get(0)); // cast exception will be given here

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.

Gonen I
sumber
5

Penindasan peringatan bukanlah solusi. Anda seharusnya tidak melakukan casting dua level dalam satu pernyataan.

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {

    // first, cast the returned Object to generic HashMap<?,?>
    HashMap<?, ?> theHash = (HashMap<?, ?>)session.getAttribute("attributeKey");

    // next, cast every entry of the HashMap to the required type <String, String>
    HashMap<String, String> returingHash = new HashMap<>();
    for (Entry<?, ?> entry : theHash.entrySet()) {
        returingHash.put((String) entry.getKey(), (String) entry.getValue());
    }
    return returingHash;
}
abbas
sumber
1
Pertanyaannya yang berumur lima tahun? Apakah Anda perlu melakukan banyak pekerjaan? Mengingat Java memiliki tipe erasure, hashmap kedua harus identik dengan yang pertama saat runtime; Saya pikir itu akan lebih efisien dan menghindari salinan jika Anda hanya mengulangi entri dan memverifikasi bahwa mereka semua adalah contoh string. Atau, TBH, periksalah sumber servlet JAR yang Anda gunakan dan verifikasi hanya dengan menempatkan string.
Rup
1
Sampai hari ini saya melihat peringatan ini di proyek-proyek. Masalahnya bukan verifikasi tipe, tetapi peringatan yang disebabkan oleh "memasukkan" ke peta yang belum diputar.
abbas
2

Tebakan cepat jika Anda memposting kode Anda dapat mengatakan dengan pasti tetapi Anda mungkin telah melakukan sesuatu di sepanjang baris

HashMap<String, Object> test = new HashMap();

yang akan menghasilkan peringatan saat Anda perlu melakukannya

HashMap<String, Object> test = new HashMap<String, Object>();

mungkin layak untuk dilihat

Generik dalam Bahasa Pemrograman Java

jika Anda tidak terbiasa dengan apa yang perlu dilakukan.

Mark Davidson
sumber
1
Sayangnya situasinya tidak mudah. Kode ditambahkan.
skiphoppy
1
Saya datang ke sini mencari jawaban untuk masalah yang sedikit berbeda: dan Anda memberi tahu saya apa yang saya butuhkan! Terima kasih!
staticsan
2

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 HashMapbukan Map<KeyType,ValueType>. Bahkan, saya tidak bisa membayangkan apa alasan untuk mengatur jenis variabel untuk HashMapbukan Map.

Dan mengapa sumbernya adalah Object? Apakah ini tipe parameter koleksi lawas? Jika demikian, gunakan obat generik dan tentukan jenis yang Anda inginkan.

phihag
sumber
2
Saya cukup yakin bahwa beralih ke Peta dalam hal ini tidak akan mengubah apa pun, tetapi terima kasih atas tip pemrogramannya, yang dapat mengubah cara saya melakukan beberapa hal, menjadi lebih baik. Sumber objek adalah API yang saya tidak punya kendali atas (kode ditambahkan).
skiphoppy
2

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.

Fortyrunner
sumber
2

Ambil yang ini, ini jauh lebih cepat daripada membuat HashMap baru, jika sudah satu, tapi masih aman, karena setiap elemen diperiksa terhadap tipenya ...

@SuppressWarnings("unchecked")
public static <K, V> HashMap<K, V> toHashMap(Object input, Class<K> key, Class<V> value) {
       assert input instanceof Map : input;

       for (Map.Entry<?, ?> e : ((HashMap<?, ?>) input).entrySet()) {
           assert key.isAssignableFrom(e.getKey().getClass()) : "Map contains invalid keys";
           assert value.isAssignableFrom(e.getValue().getClass()) : "Map contains invalid values";
       }

       if (input instanceof HashMap)
           return (HashMap<K, V>) input;
       return new HashMap<K, V>((Map<K, V>) input);
    }
jfreundo
sumber
key.isAssignableFrom(e.getKey().getClass())dapat ditulis sebagaikey.isInstance(e.getKey())
user102008
1

Cukup ketik centang sebelum Anda melemparkannya.

Object someObject = session.getAttribute("attributeKey");
if(someObject instanceof HashMap)
HashMap<String, String> theHash = (HashMap<String, String>)someObject;  

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.

Rick
sumber
8
Sayangnya, obat generik membuat hal itu mustahil. Ini bukan hanya HashMap, ini adalah HashMap dengan informasi jenis. Dan jika saya menghilangkan informasi itu, saya hanya akan mendorong peringatan ke tempat lain.
skiphoppy
1

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:

public final class Items implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private Map<String,String> map;
    public Items(Map<String,String> map) {
        this.map = New.immutableMap(map);
    }
    public Map<String,String> getMap() {
        return map;
    }
    @Override public String toString() {
        return map.toString();
    }
}

public final class New {
    public static <K,V> Map<K,V> immutableMap(
        Map<? extends K, ? extends V> original
    ) {
        // ... optimise as you wish...
        return Collections.unmodifiableMap(
            new HashMap<String,String>(original)
        );
    }
}

static Map<String, String> getItems(HttpSession session) {
    Items items = (Items)
        session.getAttribute("attributeKey");
    return items.getMap();
}

* Kecuali terlalu banyak level tipuan.

Tom Hawtin - tackline
sumber
1
Kutipan ini dikaitkan dengan almarhum Profesor David Wheeler. en.wikipedia.org/wiki/…
Stephen C
1

Inilah satu cara saya menangani ini ketika saya menimpa equals()operasi.

public abstract class Section<T extends Section> extends Element<Section<T>> {
    Object attr1;

    /**
    * Compare one section object to another.
    *
    * @param obj the object being compared with this section object
    * @return true if this section and the other section are of the same
    * sub-class of section and their component fields are the same, false
    * otherwise
    */       
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            // this exists, but obj doesn't, so they can't be equal!
            return false;
        }

        // prepare to cast...
        Section<?> other;

        if (getClass() != obj.getClass()) {
            // looks like we're comparing apples to oranges
            return false;
        } else {
            // it must be safe to make that cast!
            other = (Section<?>) obj;
        }

        // and then I compare attributes between this and other
        return this.attr1.equals(other.attr1);
    }
}

Ini tampaknya berfungsi di Java 8 (bahkan dikompilasi dengan -Xlint:unchecked)

Jim Daehn
sumber
0

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

HashMap<?,?> getItems(javax.servlet.http.HttpSession session) {  
    HashMap<?,?> theHash = (HashMap<?,?>)session.getAttribute("attributeKey");
    return theHash;
} 

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.

Lukas Normantas
sumber
0

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.

package scratchpad;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

public class YellowMouse {

    // First way

    Map<String, String> getHashMapStudiouslyAvoidingSuppressTag(HttpSession session) {
      Map<?, ?> theHash = (Map<?, ?>)session.getAttribute("attributeKey");

      Map<String, String> yellowMouse = new HashMap<String, String>();
      for( Map.Entry<?, ?> entry : theHash.entrySet() ){
        yellowMouse.put( (String)entry.getKey(), (String)entry.getValue() );
      }

      return yellowMouse;
    }


    // Second way

    Map<String, String> getHashMapUsingNaughtyButNiceUtilityMethod(HttpSession session) {
      return uncheckedCast( session.getAttribute("attributeKey") );
    }


    // NB this is a utility method which should be kept in your utility library. If you do that it will
    // be the *only* time in your entire life that you will have to use this particular tag!!

    @SuppressWarnings({ "unchecked" })
    public static synchronized <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }


}
mike rodent
sumber
fakta bahwa Anda tidak memiliki hak komentar tidak memungkinkan Anda untuk mengedit jawaban orang lain untuk menambahkan komentar Anda; Anda mengedit jawaban orang lain untuk meningkatkannya dalam format, sintaksis, ..., bukan untuk menambahkan pendapat Anda pada mereka. Ketika Anda akan mencapai 50 rep, Anda akan dapat berkomentar di mana-mana, sementara itu saya cukup yakin Anda bisa menolak (atau, jika Anda benar-benar tidak bisa, tulis komentar Anda untuk jawaban yang ada di posting Anda). (catatan untuk orang lain: Saya menulis ini karena saya melihat - dan menolak - usulan komentarnya - suntingan ke pos lain di alat moderasi)
Matteo Italia
-1

Ini membuat peringatan hilang ...

 static Map<String, String> getItems(HttpSession session) {
        HashMap<?, ?> theHash1 = (HashMap<String,String>)session.getAttribute("attributeKey");
        HashMap<String,String> theHash = (HashMap<String,String>)theHash1;
    return theHash;
}
suam-suam kuku
sumber
1
Tidak, tidak. Sebenarnya, ini menciptakan dua peringatan di mana pertama ada satu.
Stijn de Witt
Ah, baiklah. Tidak yakin mengapa saya memikirkan itu.
suam
-3

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.

Marc Riehm
sumber
9
Bolehkah saya bertanya mengapa? menonaktifkan peringatan secara global akan menyembunyikan tempat lain di mana masalah ini nyata. menambahkan @SuppressWarningstidak membuat kode tidak dapat dibaca sama sekali.
MByD