692 Stimmen

Wie gehe ich mit ungeprüften Besetzungswarnungen um?

Eclipse gibt mir eine Warnung der folgenden Form:

Typ Sicherheit: Ungeprüfter Cast von Object nach HashMap

Dies stammt aus einem Aufruf an eine API, über die ich keine Kontrolle habe und die Object zurückgibt:

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

Ich möchte Eclipse-Warnungen nach Möglichkeit vermeiden, da sie theoretisch zumindest auf ein potenzielles Codeproblem hinweisen. Ich habe allerdings noch keine gute Möglichkeit gefunden, dieses Problem zu beseitigen. Ich kann die einzelne betroffene Zeile in eine eigene Methode auslagern und Folgendes hinzufügen @SuppressWarnings("unchecked") zu dieser Methode, wodurch die Auswirkungen eines Codeblocks, in dem ich Warnungen ignoriere, begrenzt werden. Gibt es bessere Möglichkeiten? Ich möchte diese Warnungen in Eclipse nicht ausschalten.

Bevor ich zu dem Code kam, war es einfacher, aber es gab trotzdem Warnungen:

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

Das Problem war, dass an anderer Stelle, wenn man versuchte, den Hash zu verwenden, Warnungen angezeigt wurden:

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.

0 Stimmen

Wenn Sie HttpSession auf diese Weise verwenden, lesen Sie den Artikel von Brian Goetz zu diesem Thema: ibm.com/developerworks/library/j-jtp09238.html

0 Stimmen

Wenn ein ungeprüfter Cast unvermeidbar ist, ist es eine gute Idee, ihn mit etwas zu koppeln, das seinen Typ logisch repräsentiert (wie ein enum oder sogar Instanzen von Class<T> ), so dass Sie einen Blick darauf werfen können und savoir es ist sicher.

4 Stimmen

610voto

Michael Myers Punkte 183216

Die offensichtliche Antwort ist natürlich, dass man den unkontrollierten Wurf nicht machen sollte.

Wenn es unbedingt notwendig ist, dann sollten Sie zumindest versuchen, den Umfang der @SuppressWarnings Bemerkung. Gemäß seiner Javadocs kann sie auf lokale Variablen angewendet werden; auf diese Weise wirkt sie sich nicht einmal auf die gesamte Methode aus.

Beispiel:

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

Es gibt keine Möglichkeit festzustellen, ob die Map wirklich die allgemeinen Parameter haben sollte <String, String> . Sie müssen vorher wissen, wie die Parameter lauten sollen (oder Sie werden es herausfinden, wenn Sie eine ClassCastException ). Aus diesem Grund erzeugt der Code eine Warnung, da der Compiler unmöglich wissen kann, ob er sicher ist.

126 Stimmen

+1 für den Hinweis, dass es auf lokale Variablen gehen kann. Eclipse bietet nur, um es auf die gesamte Methode hinzufügen...

17 Stimmen

Eclipse 3.7 (Indigo) bietet Unterstützung für das Hinzufügen von "unchecked" zu lokalen Variablen.

0 Stimmen

@thSoft Ist das so, weil das nur mit neueren Versionen von Java funktioniert?

187voto

Julien Chastang Punkte 17256

Leider gibt es hier keine großartigen Möglichkeiten. Denken Sie daran, dass das Ziel all dieser Maßnahmen darin besteht, die Sicherheit des Typs zu erhalten. " Java-Generika " bietet eine Lösung für den Umgang mit nicht-generierten Legacy-Bibliotheken, und es gibt insbesondere eine, die "Leerschleifentechnik" in Abschnitt 8.2. Im Grunde machen Sie den unsicheren Cast und unterdrücken die Warnung. Führen Sie dann eine Schleife durch die Map wie folgt:

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

Wenn ein unerwarteter Typ angetroffen wird, erhalten Sie eine Laufzeit ClassCastException aber zumindest wird es in der Nähe der Problemquelle geschehen.

7 Stimmen

Eine viel, viel bessere Antwort als die von skiphoppy, und zwar aus mehreren Gründen: 1) Dieser Code ist viel, viel kürzer. 2) Dieser Code wirft tatsächlich ClassCastException wie erwartet. 3) Dieser Code führt keine vollständige Kopie der Source-Map durch. 4) Die Schleifen können leicht in einer separaten Methode verpackt werden, die in einem Assert verwendet wird, wodurch die Leistungseinbußen im Produktionscode leicht beseitigt würden.

6 Stimmen

Besteht nicht die Möglichkeit, dass der Java-Compiler oder der JIT-Compiler entscheidet, dass die Ergebnisse dieses Codes nicht verwendet werden und ihn "optimiert", indem er ihn nicht kompiliert?

1 Stimmen

Es ist nicht wirklich toter Code, wenn er möglicherweise eine Ausnahme auslösen kann. Ich weiß nicht genug über die heute verwendeten JIT-Compiler, um garantieren zu können, dass keiner von ihnen dies vermasseln würde, aber ich kann mit ziemlicher Sicherheit sagen, dass sie es nicht tun sollten.

114voto

skiphoppy Punkte 89474

Wow; ich glaube, ich habe die Antwort auf meine eigene Frage gefunden. Ich bin mir nur nicht sicher, ob es das wert ist! :)

Das Problem ist, dass die Besetzung nicht überprüft wird. Sie müssen es also selbst überprüfen. Sie können einen parametrisierten Typ nicht einfach mit instanceof überprüfen, da die Informationen über den parametrisierten Typ zur Laufzeit nicht verfügbar sind, da sie zur Kompilierzeit gelöscht wurden.

Aber Sie können jedes einzelne Element im Hash mit instanceof überprüfen und so einen neuen Hash konstruieren, der typsicher ist. Und Sie werden keine Warnungen provozieren.

Dank mmyers und Esko Luontola habe ich den Code, den ich ursprünglich hier geschrieben habe, parametrisiert, so dass er irgendwo in eine Utility-Klasse verpackt und für jede parametrisierte HashMap verwendet werden kann. Wenn Sie es besser verstehen wollen und nicht sehr vertraut mit Generika sind, empfehle ich Ihnen, die Bearbeitungshistorie dieser Antwort anzusehen.

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

Das ist eine Menge Arbeit, möglicherweise für sehr wenig Lohn... Ich bin mir nicht sicher, ob ich es benutzen werde oder nicht. Ich würde mich über Kommentare freuen, ob die Leute denken, dass es das wert ist oder nicht. Außerdem würde ich mich über Verbesserungsvorschläge freuen: Gibt es etwas Besseres, das ich tun kann, als AssertionErrors zu werfen? Gibt es etwas Besseres, das ich auslösen könnte? Sollte ich es eine geprüfte Exception machen?

1 Stimmen

Sie können es nicht parametrisieren, es sei denn, Sie fügen auch zwei Klassenparameter hinzu. Ihre Signatur würde lauten "public static <K,V> HashMap<K,V> checkHash(HashMap<?,?> input, Class<K> keyClass, Class<V> valueClass)". Dann würden Sie keyClass.isAssignableFrom(key.getClass()) und keyClass.cast(key) verwenden.

0 Stimmen

Ich bin mir nicht sicher, ob es sich lohnt; es kommt auf den Kontext an. Eine Alternative wäre, die Methode wie folgt zu deklarieren: Map<?,?> getItems() und die Umwandlung in String im Client-Code. Die korrekte Verwendung von Generics stellt sicher, dass eine ClassCastException niemals ausgelöst wird, wenn kein expliziter Cast erfolgt.

71 Stimmen

Dieses Zeug ist verwirrend, aber ich denke, alles, was Sie getan haben, ist ClassCastExceptions für AssertionErrors ausgetauscht.

52voto

Dave Punkte 4488

In Eclipse Preferences, gehen Sie zu Java->Compiler->Errors/Warnings->Generic types und markieren Sie die Ignore unavoidable generic type problems Kontrollkästchen.

Dies entspricht dem Zweck der Frage, d. h.

Ich würde gerne Eclipse-Warnungen vermeiden...

wenn nicht der Geist.

1 Stimmen

Ah, danke dafür :) Ich bekam ein " uses unchecked or unsafe operations. " Fehler in javac sondern das Hinzufügen von @SuppressWarnings("unchecked") machte Eclipse unglücklich und behauptete, die Unterdrückung sei unnötig. Deaktivieren Sie diese Box macht Eclipse und javac verhalten sich genauso, wie ich es wollte. Die explizite Unterdrückung der Warnung im Code ist viel klarer als die Unterdrückung der Warnung überall in Eclipse.

29voto

Esko Luontola Punkte 71758

Sie können eine Hilfsklasse wie die folgende erstellen und sie verwenden, um die ungeprüfte Warnung zu unterdrücken.

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

Sie können es wie folgt verwenden:

import static Objects.uncheckedCast;
...

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

Weitere Informationen zu diesem Thema finden Sie hier: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html

22 Stimmen

Nicht abzustimmen, aber der Wrapper bringt genau nichts anderes als die Unterdrückung der Warnung.

4 Stimmen

+1, da bei dieser Lösung keine wertvollen Codezeilen verschwendet werden.

1 Stimmen

@ErikE Zu viel. Viel teurere, größere und höher auflösende Monitore, um all den verschwendeten Zeilen Platz zu geben, ein größeres Pult, um all diese größeren Monitore aufzustellen, ein größerer Raum, um das größere Pult aufzustellen, und ein einsichtiger Chef

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X