Kontrol edilmeyen yayın uyarılarını nasıl ele alabilirim?


611

Eclipse bana aşağıdaki form hakkında bir uyarı veriyor:

Tip güvenliği: Nesneden HashMap'a işaretlenmemiş döküm

Bu bir API çağrısından hangi Object döndürür üzerinde hiçbir kontrol var ki:

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

Mümkünse Eclipse uyarılarından kaçınmak istiyorum, çünkü teorik olarak en azından potansiyel bir kod problemini gösteriyorlar. Yine de bunu ortadan kaldırmak için iyi bir yol bulamadım. Tek bir yöntemi tek başına bir yönteme çıkarabilir ve @SuppressWarnings("unchecked")bu yönteme ekleyebilirim , böylece uyarıları görmezden geldiğim bir kod bloğuna sahip olmanın etkisini sınırlayabilirim. Daha iyi seçenekler var mı? Eclipse'de bu uyarıları kapatmak istemiyorum.

Ben koda gelmeden önce, daha basit, ama yine de uyarılmış uyarılar:

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

Sorun, hash'ı kullanmaya çalıştığınızda başka yerlerde oldu.

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.

HttpSession'ı bu şekilde kullanıyorsanız, Brian
Goetz'un

Denetlenmeyen bir döküm kaçınılmazsa, iyi bir fikir, türünü mantıksal olarak temsil eden bir şeyle (bir enumveya hatta örnekleri gibi Class<T>) sıkıca birleştirmektir , böylece ona bakabilir ve güvenli olduğunu bilirsiniz .
Philip Guin



Eklerdim, sadece rahatsız edici kodu içeren yöntem düzeyinde @SuppressWarnings ("işaretlenmemiş") ekleyebiliriz bulundu. Bu yüzden bunu yapmak zorunda olduğum bir rutine kodu kırdı. Bunu her zaman söz konusu çizginin hemen üzerinde yapabileceğinizi düşündüm.
JGFMK

Yanıtlar:


557

Açık cevap, elbette, kontrolsüz oyuncu seçimi yapmak değildir.

Kesinlikle gerekliyse, en azından @SuppressWarningsek açıklamanın kapsamını sınırlamaya çalışın . Javadoclarına göre yerel değişkenlere devam edebilir; bu şekilde, tüm yöntemi bile etkilemez.

Misal:

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

MapGerçekten genel parametrelere sahip olup olmayacağını belirlemenin bir yolu yoktur <String, String>. Önceden parametrelerin ne olması gerektiğini bilmelisiniz (ya da a aldığınızda öğreneceksiniz ClassCastException). Bu nedenle kod bir uyarı oluşturur, çünkü derleyici güvenli olup olmadığını bilemez.


112
Yerel değişkenlere gidebileceğini belirtmek için +1. Eclipse sadece tüm yönteme eklemeyi teklif ediyor ...
thSoft

17
Eclipse 3.7 (Indigo), yerel değişkenlere kontrolsüz ekleme desteğine sahiptir.
sweetfa

78
Uyarı sadece derleyicinin dökümün güvenli olduğunu bilmemesi değil. Örneğin String s = (String) new Object() ;, derleyici, dökümün güvenli olduğunu bilmese bile uyarı almaz. Uyarı, derleyici (a) kalıbın güvenli olduğunu bilmiyor VE (b) kalıbın tam bir çalışma zamanı kontrolü oluşturmayacaktır. Bir olduğu bir onay olacak Hashmap, ama bir olduğunu bir çek olmayacak HashMap<String,String>.
Theodore Norvell

9
Ne yazık ki, kadro ve uyarı ödev için olsa da , ek açıklama değişken bildirime devam etmelidir ... Yani beyan ve ödev farklı yerlerde ise (örneğin, sırasıyla 'deneyin' bloğunun dışında ve içinde) , Eclipse artık iki uyarı üretiyor: orijinal işaretlenmemiş yayın ve yeni bir "gereksiz ek açıklama" tanılaması.
Ti Strga

6
Gerçek dökümden farklı bir satırda farklı bir kapsamda olabilen yerel değişken bildirimine eşlik etmesi gereken ek açıklama için geçici bir çözüm, özellikle aynı satırda döküm gerçekleştirmek için döküm kapsamı dahilinde yerel bir değişken oluşturmaktır. deklarasyon olarak. Ardından bu değişkeni farklı bir kapsamdaki gerçek değişkene atayın. Bu, ek açıklama burada uygulanamayacağından, örnek değişkenine bir yayınlama uyarısını bastırmak için de kullandığım yöntemdir.
Jeff Lockhart

168

Ne yazık ki, burada harika seçenekler yok. Unutmayın, tüm bunların amacı tip güvenliğini korumaktır. " Java Generics " jenerikleştirilmemiş eski kütüphanelerle uğraşmak için bir çözüm sunar ve özellikle bölüm 8.2'de "boş döngü tekniği" olarak adlandırılan bir tane vardır. Temel olarak, güvenli olmayan bir döküm yapın ve uyarıyı bastırın. Ardından harita üzerinde şu şekilde gezinin:

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

Beklenmeyen bir türle karşılaşılırsa, bir çalışma zamanı alırsınız ClassCastException, ancak en azından sorunun kaynağına yakın olur.


6
Birden fazla nedenden ötürü, skiphoppy tarafından verilen yanıttan çok, çok daha iyi bir cevap: 1) Bu kod çok, çok daha kısadır. 2) Bu kod aslında ClassCastException beklendiği gibi atar. 3) Bu kod kaynak haritanın tam bir kopyasını yapmaz. 4) Döngüler, bir üretim kodunda kullanılan ayrı bir yöntemle kolayca sarılabilir, bu da üretim kodunda isabet alan performansı kolayca kaldırabilir.
Stijn de Witt

6
Java derleyicisinin veya JIT derleyicisinin bu kodun sonuçlarının kullanılmadığına karar vermesi ve derlemeyerek "optimize etmesi" olasılığı yok mu?
RenniePet

1
Potansiyel bir istisna atabiliyorsa gerçekten ölü kod değildir. Bugün kullanılan JIT derleyicileri hakkında bunların hiçbirinin bunu bertaraf etmeyeceğini garanti etmek için yeterince bilgim yok, ancak gerekmediğini söylerken kendime oldukça güveniyorum.
GrandOpener

3
Aynı harita hala kullanıldığı için bu hala tip güvenliğini garanti etmez. Başlangıçta, sadece Dizeler ve Sayılar içeren Harita <Nesne, Nesne> olarak tanımlanmış olabilir ve daha sonra bir Boole eklenirse, bu kodun kullanıcısı kafa karıştırıcı ve izini sürmek oldukça zor olacaktır. Tip güvenliğini garanti etmenin tek yolu, haritayı içine girmesine izin verilen talebi garanti eden yeni bir haritaya kopyalamaktır.
user2219808

112

Vay; Sanırım kendi sorumun cevabını buldum. Buna değdiğinden emin değilim! :)

Sorun, oyuncuların kontrol edilmemesi. Yani, kendiniz kontrol etmelisiniz. Parametrelendirilmiş bir türü instanceof ile kontrol edemezsiniz, çünkü parametreli tip bilgisi çalışma zamanında kullanılamaz, derleme zamanında silinmiştir.

Ancak, karmadaki her bir öğe için instanceof ile bir kontrol gerçekleştirebilirsiniz ve bunu yaparken, tür güvenli olan yeni bir karma oluşturabilirsiniz. Ve hiçbir uyarıyı kışkırtmayacaksınız.

Mmyers ve Esko Luontola sayesinde, burada yazdığım kodu parametrelendirdim, bu yüzden bir yerde bir yardımcı program sınıfına sarılabilir ve herhangi bir parametreli HashMap için kullanılabilir. Daha iyi anlamak ve jeneriklere çok aşina değilseniz, bu cevabın düzenleme geçmişini görüntülemenizi öneririz.

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;
}

Bu çok fazla iş, muhtemelen çok az ödül için ... Kullanıp kullanamayacağımdan emin değilim. İnsanların buna değip değmeyeceğini düşünen herhangi bir yorumu takdir ediyorum. Ayrıca, iyileştirme önerilerini takdir ediyorum: AssertionErrors atmanın yanı sıra yapabileceğim daha iyi bir şey var mı? Atabileceğim daha iyi bir şey var mı? İşaretli bir İstisna yapmalı mıyım?


68
Bu şeyler kafa karıştırıcı, ama yaptığınız tüm AssertionErrors için ClassCastExceptions ticareti olduğunu düşünüyorum.
Dustin Getz

59
Dostum, buna kesinlikle değmez! Geri dönüp içeride o karmaşa ile bazı kod değiştirmek zorunda zavallı özü düşünün. Uyarıları bastırmayı sevmiyorum, ama bence bu daha az kötülük.
Craig B

69
Bu sadece çirkin, kafa karıştırıcı, karışıklık değil (bir bol yorumdan kaçınamadığınızda bakım programcısı üzerinden geçebilir); koleksiyondaki her elemanın tekrarlanması, dökümü O (1) 'den O (n) işlemine dönüştürür. Bu asla beklenmeyecek bir şeydir ve kolayca korkunç bir gizem yavaşlamasına dönüşebilir.
Dan Is Fiddling By Firelight

22
@DanNeely haklısın. Genel olarak, hiç kimse bunu yapmamalıdır.
skiphoppy

4
Bazı yorumlar ... yöntem imzası yanlış çünkü lanet bir şey "dökmüyor", sadece mevcut haritayı yeni bir haritaya kopyalıyor. Ayrıca, herhangi bir haritayı kabul etmek için yeniden düzenlenebilir ve HashMap'in kendisine güvenmeyebilir (yani, dahili tür HashMap olsa bile, yöntem imzasında Map'i alın ve Map'i döndürün). Döküm veya depolamayı yeni bir haritaya yapmanız gerekmez - bir onaylama hatası atmazsanız, verilen harita şu anda içinde doğru türlere sahiptir. Genel türlerle yeni bir harita oluşturmak anlamsızdır, çünkü yine de ham yapabilir ve her şeyi koyabilirsiniz.
MetroidFan2002

51

Tutulma Tercihleri'nde Java-> Derleyici-> Hatalar / Uyarılar-> Genel türler'e gidin ve onay Ignore unavoidable generic type problemskutusunu işaretleyin.

Bu sorunun amacını karşılar, yani

Tutulma uyarılarından kaçınmak istiyorum ...

ruh değilse.


1
Ah, bunun için teşekkürler :) " uses unchecked or unsafe operations." hatası alıyordum javac, ama @SuppressWarnings("unchecked")eklenti mutsuz yaptı, bastırmanın gereksiz olduğunu iddia etti. Bu kutunun işaretini kaldırmak Eclipse ve javacaynı şekilde davranıyor, istediğim buydu. Koddaki uyarıyı açıkça bastırmak Eclipse içindeki her yerde bastırmaktan çok daha açıktır.
dimo414

26

Aşağıdaki gibi bir yardımcı program sınıfı oluşturabilir ve bu öğeyi işaretlenmemiş uyarıyı bastırmak için kullanabilirsiniz.

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;
    }
}

Aşağıdaki gibi kullanabilirsiniz:

import static Objects.uncheckedCast;
...

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

Bununla ilgili biraz daha tartışma burada: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html


18
ancak sarmalayıcı sadece uyarıyı bastırmanın üzerine hiçbir şey eklemez.
Dustin Getz

3
+1 çünkü bu çözüm değerli kod satırlarını boşa harcamaz.
Tino

1
@ErikE Çok fazla. Tüm bu israf hatlarına yer açmak için çok daha pahalı daha büyük ve daha yüksek çözünürlüklü monitörler, daha büyük monitörleri koymak için daha büyük bir masa, daha büyük masayı koymak için daha büyük bir oda ve anlayışlı bir patron ..
Tino

1
@ErikE Kaydırma çubukları, için vi? Dalgamı geçiyorsun?
Tino

21

Bu şeyler zor, ama şu anki düşüncelerim:

API'niz Nesne döndürürse, yapabileceğiniz hiçbir şey yoktur - ne olursa olsun, nesneyi körü körüne yayınlayacaksınız. Java'nın ClassCastExceptions'ı atmasına izin verirsiniz veya her bir öğeyi kendiniz kontrol edebilir ve Assertions veya IllegalArgumentExceptions veya benzerlerini atayabilirsiniz, ancak bu çalışma zamanı denetimlerinin tümü eşdeğerdir. Çalışma zamanında ne yaparsanız yapın , derleme zamanı denetlenmeyen yayınlamayı bastırmanız gerekir .

Ben sadece kör döküm tercih ve JVM benim için çalışma zamanı denetimi yapmak izin istiyorum, çünkü biz API ne dönmesi gerektiğini "bilmek", ve genellikle API çalıştığını varsayalım. İhtiyacınız olursa, jenerikleri kadronun her yerinde kullanın. Hala tek kör oyuncuya sahip olduğunuz için orada gerçekten bir şey satın almıyorsunuz, ancak en azından oradan jenerikleri kullanabilirsiniz, böylece JVM kodunuzun diğer parçalarında kör dökümlerden kaçınmanıza yardımcı olabilir.

Bu özel durumda, muhtemelen SetAttribute çağrısını görebilir ve türün girildiğini görebilirsiniz, bu yüzden sadece çıkışta türü kör döküm ahlak dışı değildir. SetAttribute'a atıfta bulunan bir yorum ekleyin ve onunla yapın.


16

Aşağıda, diğer yanıtlarda belirtilen iki stratejiyi kullanarak "denetlenmeyen döküm" uyarısını önleyen kısaltılmış bir örnek verilmiştir .

  1. İlgilenilen türün Sınıfını runtime ( Class<T> inputElementClazz) öğesinde parametre olarak iletin . Sonra şunları kullanabilirsiniz:inputElementClazz.cast(anyObject);

  2. Bir Koleksiyonun tür dökümü için joker karakter? Genel kod ( Collection<?> unknownTypeCollection) ' dan ne tür nesneler bekleyeceğinizi gerçekten bilmediğinizi kabul etmek için genel bir T türü yerine . Sonuçta, "denetlenmeyen oyuncu kadrosu" uyarısı bize söylemek istediği şey: Bir elde ettiğimizden emin olamayız Collection<T>, bu yüzden yapılacak dürüst şey a kullanmaktır Collection<?>. Kesinlikle ihtiyaç duyulursa, bilinen türde bir koleksiyon hala oluşturulabilir ( Collection<T> knownTypeCollection).

Aşağıdaki örnekte arabirim oluşturulmuş eski kod, StructuredViewer'da "input" özniteliğine sahiptir (StructuredViewer bir ağaç veya tablo gereci, "input", arkasındaki veri modelidir). Bu "girdi" herhangi bir Java Koleksiyonu olabilir.

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();
}

Doğal olarak, eski kodu yanlış veri türleriyle kullanırsak (örneğin, bir diziyi Java Koleksiyonu yerine StructuredViewer'ın "girişi" olarak ayarlarsak) yukarıdaki kod çalışma zamanı hataları verebilir.

Yöntemin çağrılmasına örnek:

dragFinishedStrategy.dragFinished(viewer, Product.class);

13

HTTP Oturum dünyasında, API bu şekilde yazıldığından (yalnızca alır ve döndürür Object) , dökümden gerçekten kaçınamazsınız .

Biraz çalışma ile kontrolsüz oyunculardan kolayca kaçınabilirsiniz '. Bu ClassCastException, bir hata durumunda orada bir hak veren geleneksel bir oyuncuya dönüşeceği anlamına gelir ). Kontrol edilmeyen bir istisna CCE, oyuncu kadrosu yerine herhangi bir noktada bir haline dönüşebilir (bunun ayrı bir uyarı olmasının nedeni budur).

HashMap'i özel bir sınıfla değiştirin:

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);
    }
}

Sonra bunun yerine bu sınıfa yayın yapın Map<String,String>ve her şey kodunuzu yazdığınız yerde kontrol edilecektir. Daha ClassCastExceptionssonra beklenmedik bir şey yok .


Bu gerçekten yararlı bir cevap.
GPrathap

10

Android Studio'da incelemeyi devre dışı bırakmak istiyorsanız şunları kullanabilirsiniz:

//noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();

2
Bu IntelliJ IDE'de de çalışıyor
neXus

8

Bu özel durumda, Haritalar'ı doğrudan HttpSession'a depolamam, bunun yerine kendi sınıfımın bir örneğini, bir Harita'yı (sınıfın bir uygulama detayı) içerir. Ardından, haritadaki öğelerin doğru türde olduğundan emin olabilirsiniz.

Ancak, yine de Harita içeriğinin doğru türde olup olmadığını kontrol etmek istiyorsanız, aşağıdaki gibi bir kod kullanabilirsiniz:

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);
    }
}

1
müthiş; Ben bunu parametreleştirmek ve uyarıları tamamen bastırmak zorunda kalmamak için cevabımla birleştirebileceğimi düşünüyorum!
skiphoppy

1
Çalışma zamanı kontrolleriyle güvenle yapmak için +1 muhtemelen en iyi tarif (anlaşılması ve bakımı kolaydır)
Tino

8

Esko Luontola tarafından verilen yanıtta Objects.Unecked utility işlevi, program dağınıklığını önlemenin harika bir yoludur.

SuppressWarnings yönteminin tamamını bir yöntemde istemiyorsanız, Java sizi yerel bir yöntem kullanmaya zorlar. Eğer bir üye için oyuncu kadrosuna ihtiyacınız varsa aşağıdaki gibi kodlara yol açabilir:

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

Yardımcı programı kullanmak çok daha temiz ve ne yaptığınızı hala belli ediyor:

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

NOT: Bazen uyarı gerçekten yanlış bir şey yaptığınız anlamına geldiğini eklemek önemli olduğunu hissediyorum:

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

Derleyicinin size söylediği şey, bu dökümün çalışma zamanında kontrol edilmeyeceğidir, bu nedenle genel kapsayıcıdaki verilere erişmeye çalışana kadar hiçbir çalışma zamanı hatası ortaya çıkmaz.


5

Uyarı bastırma bir çözüm değildir. Bir ifadede iki seviyeli döküm yapmamalısınız.

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;
}

1
Beş yaşındaki sorusu mu? O kadar işi yapman gerekiyor mu? Java'nın tür silme özelliği olduğu düşünüldüğünde, ikinci hashmap çalışma zamanında ilk hastayla aynı olmalıdır; Ben sadece daha verimli olacağını düşünüyorum ve sadece girişler üzerinden yinelenen ve tüm dizeleri örnekleri olduğunu doğruladıysanız kopya kaçının. Veya, TBH, kullandığınız sunucu JAR'ın kaynağını inceleyin ve yalnızca dizeleri koyduğunu doğrulayın.
Rup

1
Bugüne kadar bu uyarıyı projelerde görüyorum. Onun problemi türün doğrulanması değil, yayınlanmamış bir haritaya “konulması” nedeniyle ortaya çıkan bir uyarıydı.
abbas

2

Kodunuzu gönderirseniz hızlı bir tahmin kesin olarak söylenebilir, ancak satırlarında bir şeyler yapmış olabilirsiniz.

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

Yapmanız gerektiğinde uyarı üretecek

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

bakmaya değer olabilir

Java Programlama Dilinde Jenerikler

ne yapılması gerektiğini bilmiyorsanız.


1
Maalesef o kadar kolay bir durum değil. Kod eklendi.
skiphoppy

1
Buraya biraz farklı bir soruna cevap aramaya geldim: ve bana tam olarak neye ihtiyacım olduğunu söyledin! Teşekkürler!
staticsan

2

Soruyu yanlış anlamış olabilirim (bir örnek ve çevreleyen birkaç satır iyi olurdu), ama neden her zaman uygun bir arayüz (ve Java5 +) kullanmıyorsunuz? A HashMapyerine bir yere atmak istemenizin hiçbir nedeni yok Map<KeyType,ValueType>. Aslında, bunun yerine bir değişkenin türünü ayarlamak için herhangi bir neden düşünemiyorum .HashMapMap

Ve kaynak neden bir Object? Eski bir koleksiyonun parametre türü mü? Öyleyse, jenerikler kullanın ve istediğiniz türü belirtin.


2
Bu durumda Haritaya geçmenin hiçbir şeyi değiştirmeyeceğinden eminim, ancak bazı şeyleri yapma şeklimi değiştirebilecek programlama ipucu için teşekkürler, daha iyisi için. Nesnenin kaynağı, üzerinde herhangi bir kontrole sahip olmadığım (kod eklendi) bir API.
skiphoppy

2

Ben Generics desteklemeyen bir API kullanmak zorunda .. Ben mümkün olduğunca az satır ile sarıcı rutinleri bu çağrıları izole denemek ve. Daha sonra SuppressWarnings ek açıklamasını kullanıyorum ve aynı zamanda tip güvenliği dökümlerini de ekliyorum.

Bu, işleri mümkün olduğunca düzenli tutmak için kişisel bir tercihtir.


2

Bunu al, yeni bir HashMap oluşturmaktan çok daha hızlı, zaten bir tane varsa, ancak yine de her öğe türüne göre kontrol edildiğinden hala güvende ...

@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);
    }

key.isAssignableFrom(e.getKey().getClass())yazılabilirkey.isInstance(e.getKey())
user102008

1

Sadece yayınlamadan önce kontrol edin.

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

Ve soran herkes için, türden emin olmadığınız nesneleri almak oldukça yaygındır. Birçok eski "SOA" uygulaması her zaman güvenmemeniz gereken çeşitli nesnelerin etrafından geçer. (Korkular!)

EDIT Örnek kodu poster güncellemelerine uyacak şekilde bir kez değiştirdi ve bazı yorumları izleyerek instanceof'in jeneriklerle iyi oynamadığını görüyorum. Bununla birlikte, dış nesneyi doğrulamak için onayın değiştirilmesi, komut satırı derleyicisiyle iyi oynar gibi görünüyor. Düzeltilmiş örnek gönderildi.


8
Ne yazık ki, jenerikler bunu imkansız kılıyor. Bu sadece bir HashMap değil, tip bilgili bir HashMap. Ve bu bilgiyi elimden alırsam, uyarıları başka bir yere gönderirim.
skiphoppy

1

Bilgisayar Bilimi'ndeki hemen hemen her sorun, bir miktar dolaylılık * veya benzeri bir şey ekleyerek çözülebilir.

Bu nedenle, daha yüksek düzeyde olan genel olmayan bir nesneyi a Map. Bağlam olmadan çok ikna edici görünmeyecek, ama yine de:

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();
}

* Çok fazla dolaylı seviye hariç.


1
Alıntı rahmetli Profesör David Wheeler'a atfediliyor. en.wikipedia.org/wiki/…
Stephen C

1

equals()İşlemi geçersiz kıldığımda bunu ele almamın bir yolu var .

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);
    }
}

Bu Java 8'de çalışıyor gibi görünüyor (hatta derlenmiş -Xlint:unchecked)


0

Session.getAttribute () tarafından döndürülen türün HashMap olduğundan eminseniz, tam olarak bu türe yazamazsınız, ancak yalnızca genel HashMap'i kontrol etmeye güvenebilirsiniz

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

Eclipse uyarıları şaşırtacaktır, ancak elbette bu hata ayıklaması zor olabilecek çalışma zamanı hatalarına yol açabilir. Bu yaklaşımı yalnızca işlem açısından kritik olmayan bağlamlarda kullanıyorum.


0

Biri etiketi tamamen ortadan kaldıran iki yol, diğeri yaramaz ama güzel bir yardımcı yöntem kullanarak.
Sorun önceden jenerik hale getirilmiş Koleksiyonlar ...
Sanırım başparmak kuralı: "nesneleri teker teker atmak" - jenerik bir dünyada ham sınıfları kullanmaya çalışırken bunun anlamı nedir? Bu Haritada <?,?> (ve aslında JVM, Harita bile olmadığını bile bulabilir!), düşündüğünüzde onu kullanamayacağınız açıktır. Bir Map <String,?> Map2 varsa, derleyici için bir "inanç eylemi" olmasına rağmen mapS.keySet (), HashSet <String> keys = (HashSet <String>) size bir uyarı vermez (çünkü bir TreeSet) haline gelmek olabilir ... ama bu sadece bir tek inanç eylemidir.

PS benim ilk şekilde olduğu gibi yineleme itiraz "sıkıcı" ve "zaman alır", cevap "acı yok kazanç" dir: jenerik bir koleksiyon Map.Entry <String, String> s, ve hiçbir şey içerdiği garanti Başka. Bu garanti için ödeme yapmak zorundasınız. Jenerikleri sistematik olarak kullanırken bu ödeme, güzel bir şekilde, makine zamanı değil kodlama uyumluluğu biçimini alır!
Bir düşünce okulu, Eclipse'ın ayarlarını, uyarılardan ziyade, bu tür kontrolsüz döküm hataları yapmak için ayarlamanız gerektiğini söyleyebilir. Bu durumda ilk yolumu kullanmalısın.

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;
    }


}

yorum ayrıcalıklarına sahip olmamanız, yorumlarınızı eklemek için başkalarının cevaplarını düzenlemenize izin vermez; başkalarının yanıtlarını biçimlendirme, sözdizimi, ... gibi konularda iyileştirmek için düzenlersiniz. 50 temsilciye ulaştığınızda, her yere yorum yapabilirsiniz, bu arada direnebileceğinizden eminim (veya gerçekten yapamıyorsanız, yorumlarınızı gönderinizdeki mevcut cevaplara yazın). (diğerleri için not: Bunu yazdım çünkü moderatörlük araçlarındaki diğer yayınlarda önerilen yorum düzenlemelerini gördüm ve reddettim)
Matteo Italia

-1

Bu uyarıları ortadan kaldırır ...

 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;
}

1
Hayır değil. Aslında bu, ilk önce bir tane olmak üzere iki uyarı oluşturur.
Stijn de Witt

Ah tamam. Bunu neden düşündüğümden emin değilim.
lukewm

-3

Çözüm: Eclipse'de bu uyarıyı devre dışı bırakın. @SuppressWarnings etmeyin, tamamen devre dışı bırakın.

Yukarıda sunulan "çözümlerin" birçoğu hattan çıkar ve aptalca bir uyarıyı bastırmak adına kodu okunamaz hale getirir.


9
Neden olduğunu sorabilir miyim? bir uyarıyı global olarak devre dışı bırakmak, bu sorunun gerçek olduğu diğer yerleri gizler. a eklenmesi @SuppressWarningskodu okunamaz hale getirmez.
MByD
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.