Eclipse warnt mich vor folgendem Formular:
Typensicherheit: Deaktivierte Umwandlung von Objekt in HashMap
Dies ist von einem Aufruf einer API, über die ich keine Kontrolle habe, welche Objekt 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 jedoch noch keinen guten Weg gefunden, diesen zu beseitigen. Ich kann die einzelne betroffene Zeile selbst in eine Methode extrahieren und @SuppressWarnings("unchecked")
zu dieser Methode hinzufügen , wodurch die Auswirkungen eines Codeblocks begrenzt werden, in dem ich Warnungen ignoriere. Irgendwelche besseren Optionen? Ich möchte diese Warnungen in Eclipse nicht deaktivieren.
Bevor ich zum Code kam, war es einfacher, aber dennoch provozierte Warnungen:
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
Das Problem war anderswo, als Sie versuchten, den Hash zu verwenden, erhielten Sie Warnungen:
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
oder sogar Instanzen vonClass<T>
), damit Sie einen Blick darauf werfen und wissen, dass sie sicher ist.Antworten:
Die offensichtliche Antwort ist natürlich nicht, die ungeprüfte Besetzung zu machen.
Wenn dies unbedingt erforderlich ist, versuchen Sie zumindest, den Umfang der
@SuppressWarnings
Anmerkung einzuschränken. Laut seinen Javadocs kann es auf lokale Variablen gehen; Auf diese Weise wirkt sich dies nicht einmal auf die gesamte Methode aus.Beispiel:
Es gibt keine Möglichkeit zu bestimmen, ob die
Map
wirklich die generischen Parameter haben sollten<String, String>
. Sie müssen vorher wissen, wie die Parameter lauten sollen (oder Sie werden herausfinden, wann Sie eine erhaltenClassCastException
). Aus diesem Grund generiert der Code eine Warnung, da der Compiler möglicherweise nicht wissen kann, ob er sicher ist.quelle
String s = (String) new Object() ;
wird keine Warnung angezeigt, obwohl der Compiler nicht weiß, dass die Umwandlung sicher ist. Die Warnung ist, weil der Compiler (a) nicht weiß, dass die Umwandlung sicher ist UND (b) zum Zeitpunkt der Umwandlung keine vollständige Laufzeitprüfung generiert. Es wird überprüft, ob es sich um eine handeltHashmap
, aber es wird nicht überprüft, ob es sich um eine handeltHashMap<String,String>
.Leider gibt es hier keine guten Möglichkeiten. Denken Sie daran, das Ziel all dessen ist es, die Typensicherheit zu gewährleisten. " Java Generics " bietet eine Lösung für den Umgang mit nicht generisierten Legacy-Bibliotheken, und es gibt eine, die in Abschnitt 8.2 als "Leerschleifentechnik" bezeichnet wird. Machen Sie im Grunde die unsichere Besetzung und unterdrücken Sie die Warnung. Dann durchlaufen Sie die Karte wie folgt:
Wenn ein unerwarteter Typ auftritt, erhalten Sie eine Laufzeit
ClassCastException
, die jedoch zumindest in der Nähe der Ursache des Problems auftritt.quelle
Beeindruckend; Ich glaube, ich habe die Antwort auf meine eigene Frage herausgefunden. Ich bin mir nur nicht sicher, ob es sich lohnt! :) :)
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 parametrisierten Typinformationen zur Laufzeit nicht verfügbar sind, da sie zur Kompilierungszeit gelöscht wurden.
Sie können jedoch jedes Element im Hash mit instanceof überprüfen und dabei einen neuen Hash erstellen, 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, sodass er irgendwo in eine Utility-Klasse eingepackt und für jede parametrisierte HashMap verwendet werden kann. Wenn Sie es besser verstehen möchten und mit Generika nicht sehr vertraut sind, empfehlen wir Ihnen, den Bearbeitungsverlauf dieser Antwort anzuzeigen.
Das ist viel Arbeit, möglicherweise für sehr wenig Belohnung ... Ich bin mir nicht sicher, ob ich es verwenden werde oder nicht. Ich würde mich über Kommentare freuen, ob die Leute denken, dass es sich lohnt oder nicht. Ich würde mich auch über Verbesserungsvorschläge freuen: Gibt es etwas Besseres, als AssertionErrors zu werfen? Gibt es etwas Besseres, das ich werfen könnte? Soll ich es zu einer geprüften Ausnahme machen?
quelle
Gehen Sie in den Eclipse-Einstellungen zu Java-> Compiler-> Fehler / Warnungen-> Generische Typen und aktivieren Sie das
Ignore unavoidable generic type problems
Kontrollkästchen.Dies erfüllt die Absicht der Frage, dh
wenn nicht der Geist.
quelle
uses unchecked or unsafe operations.
" Fehler erhaltenjavac
, aber das Hinzufügen@SuppressWarnings("unchecked")
machte Eclipse unglücklich und behauptete, die Unterdrückung sei unnötig. Wenn Sie dieses Kontrollkästchen deaktivieren , wird Eclipse aktiviert undjavac
verhält sich genauso, wie ich es wollte. Das explizite Unterdrücken der Warnung im Code ist viel klarer als das Unterdrücken überall in Eclipse.Sie können eine Dienstprogrammklasse wie die folgende erstellen und damit die nicht aktivierte Warnung unterdrücken.
Sie können es wie folgt verwenden:
Weitere Diskussionen hierzu finden Sie hier: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
quelle
vi
? Machst du Witze?Dieses Zeug ist schwer, aber hier sind meine aktuellen Gedanken:
Wenn Ihre API Object zurückgibt, können Sie nichts tun - egal was passiert, Sie werden das Objekt blind umwandeln. Sie lassen Java ClassCastExceptions auslösen, oder Sie können jedes Element selbst überprüfen und Assertions oder IllegalArgumentExceptions oder ähnliches auslösen, aber diese Laufzeitprüfungen sind alle gleichwertig. Sie müssen die ungeprüfte Umwandlung der Kompilierungszeit unterdrücken , unabhängig davon, was Sie zur Laufzeit tun.
Ich würde es einfach vorziehen, blind zu besetzen und die JVM ihre Laufzeitprüfung für mich durchführen zu lassen, da wir "wissen", was die API zurückgeben soll, und normalerweise bereit sind anzunehmen, dass die API funktioniert. Verwenden Sie Generika überall über der Besetzung, wenn Sie sie benötigen. Sie kaufen dort eigentlich nichts, da Sie immer noch den Single Blind Cast haben, aber zumindest können Sie von da an Generika verwenden, damit die JVM Ihnen hilft, Blind Casts in anderen Teilen Ihres Codes zu vermeiden.
In diesem speziellen Fall können Sie vermutlich den Aufruf von SetAttribute sehen und sehen, dass der Typ eingeht. Es ist also nicht unmoralisch, den Typ auf dem Weg nach draußen blind zu machen. Fügen Sie einen Kommentar hinzu, der auf das SetAttribute verweist, und fertig.
quelle
Hier ist ein verkürztes Beispiel, das die Warnung "ungeprüfte Besetzung" vermeidet, indem zwei in anderen Antworten erwähnte Strategien angewendet werden.
Übergeben Sie zur Laufzeit (
Class<T> inputElementClazz
) die Klasse des interessierenden Typs als Parameter . Dann können Sie verwenden:inputElementClazz.cast(anyObject);
Verwenden Sie für das Typ-Casting einer Sammlung den Platzhalter? anstelle eines generischen Typs T, um zu bestätigen, dass Sie tatsächlich nicht wissen, welche Art von Objekten Sie vom Legacy-Code erwarten sollen (
Collection<?> unknownTypeCollection
). Immerhin ist es das, was die Warnung "ungeprüfte Besetzung" uns sagen will: Wir können nicht sicher sein, dass wir eine bekommenCollection<T>
, also ist es ehrlich, eine zu verwendenCollection<?>
. Bei Bedarf kann weiterhin eine Sammlung eines bekannten Typs erstellt werden (Collection<T> knownTypeCollection
).Der im folgenden Beispiel angeschlossene Legacy-Code hat im StructuredViewer das Attribut "input" (StructuredViewer ist ein Baum- oder Tabellen-Widget, "input" ist das dahinter stehende Datenmodell). Diese "Eingabe" kann jede Art von Java-Sammlung sein.
Natürlich kann der obige Code Laufzeitfehler verursachen, wenn wir den Legacy-Code mit den falschen Datentypen verwenden (z. B. wenn wir anstelle einer Java-Sammlung ein Array als "Eingabe" des StructuredViewer festlegen).
Beispiel für den Aufruf der Methode:
quelle
In der HTTP-Sitzungswelt können Sie die Umwandlung nicht wirklich vermeiden, da die API so geschrieben ist (nur Takes und Returns
Object
).Mit ein wenig Arbeit können Sie jedoch die ungeprüfte Besetzung leicht vermeiden. Dies bedeutet, dass es sich in eine traditionelle Besetzung verwandelt, die
ClassCastException
im Fehlerfall ein Recht gibt. Eine ungeprüfte Ausnahme kann zu einemCCE
späteren Zeitpunkt anstelle des Besetzungspunkts zu einer Ausnahme werden (aus diesem Grund handelt es sich um eine separate Warnung).Ersetzen Sie die HashMap durch eine dedizierte Klasse:
Dann in diese Klasse
Map<String,String>
umwandeln und alles wird genau an der Stelle überprüft, an der Sie Ihren Code schreiben. Kein unerwartetesClassCastExceptions
später.quelle
Wenn Sie in Android Studio die Inspektion deaktivieren möchten, können Sie Folgendes verwenden:
quelle
In diesem speziellen Fall würde ich Maps nicht direkt in der HttpSession speichern, sondern eine Instanz meiner eigenen Klasse, die wiederum eine Map (ein Implementierungsdetail der Klasse) enthält. Dann können Sie sicher sein, dass die Elemente in der Karte vom richtigen Typ sind.
Wenn Sie jedoch trotzdem überprüfen möchten, ob der Inhalt der Karte vom richtigen Typ ist, können Sie einen Code wie den folgenden verwenden:
quelle
Die Dienstprogrammfunktion Objects.Unchecked in der obigen Antwort von Esko Luontola ist eine hervorragende Möglichkeit, Programmunordnung zu vermeiden.
Wenn Sie die SuppressWarnings nicht für eine gesamte Methode verwenden möchten, werden Sie von Java gezwungen, sie auf eine lokale Methode zu setzen. Wenn Sie eine Besetzung für ein Mitglied benötigen, kann dies zu folgendem Code führen:
Die Verwendung des Dienstprogramms ist viel sauberer und es ist immer noch offensichtlich, was Sie tun:
HINWEIS: Ich halte es für wichtig hinzuzufügen, dass die Warnung manchmal wirklich bedeutet, dass Sie etwas falsch machen, wie:
Der Compiler teilt Ihnen mit, dass diese Umwandlung zur Laufzeit NICHT überprüft wird, sodass kein Laufzeitfehler ausgelöst wird, bis Sie versuchen, auf die Daten im generischen Container zuzugreifen.
quelle
Die Unterdrückung von Warnungen ist keine Lösung. Sie sollten kein zweistufiges Casting in einer Anweisung durchführen.
quelle
Eine schnelle Vermutung, ob Sie Ihren Code veröffentlichen, kann sicher sagen, aber Sie haben möglicherweise etwas in der Art von getan
Dies wird die Warnung erzeugen, wenn Sie dies tun müssen
es könnte einen Blick wert sein
Generika in der Programmiersprache Java
wenn Sie nicht wissen, was zu tun ist.
quelle
Ich habe die Frage vielleicht falsch verstanden (ein Beispiel und ein paar umgebende Zeilen wären nett), aber warum verwenden Sie nicht immer eine geeignete Schnittstelle (und Java5 +)? Ich sehe keinen Grund, warum Sie jemals zu einem
HashMap
statt zu einem werfen möchtenMap<KeyType,ValueType>
. Tatsächlich kann ich mir keinen Grund vorstellen , den Typ einer Variablen aufHashMap
anstatt zu setzenMap
.Und warum ist die Quelle eine
Object
? Ist es ein Parametertyp einer Legacy-Sammlung? Verwenden Sie in diesem Fall Generika und geben Sie den gewünschten Typ an.quelle
Wenn ich eine API verwenden muss, die Generics nicht unterstützt. Ich versuche, diese Aufrufe in Wrapper-Routinen mit so wenig Zeilen wie möglich zu isolieren. Ich verwende dann die SuppressWarnings-Annotation und füge gleichzeitig die Typensicherheitsabdrücke hinzu.
Dies ist nur eine persönliche Präferenz, um die Dinge so ordentlich wie möglich zu halten.
quelle
Nehmen wir diese, es ist viel schneller als das Erstellen einer neuen HashMap, wenn es bereits eine ist, aber immer noch sicher, da jedes Element gegen seinen Typ geprüft wird ...
quelle
key.isAssignableFrom(e.getKey().getClass())
kann geschrieben werden alskey.isInstance(e.getKey())
Überprüfen Sie es einfach, bevor Sie es wirken.
Und für alle, die fragen, ist es durchaus üblich, Objekte zu erhalten, bei denen Sie sich nicht sicher sind, welchen Typ sie haben. Viele ältere "SOA" -Implementierungen geben verschiedene Objekte weiter, denen Sie nicht immer vertrauen sollten. (Die Schrecken!)
BEARBEITEN Der Beispielcode wurde einmal geändert, um ihn an die Aktualisierungen des Posters anzupassen. Nach einigen Kommentaren sehe ich, dass die Instanz von nicht gut mit Generika funktioniert. Das Ändern der Prüfung zur Validierung des äußeren Objekts scheint jedoch mit dem Befehlszeilen-Compiler gut zu funktionieren. Überarbeitetes Beispiel jetzt veröffentlicht.
quelle
Fast jedes Problem in der Informatik kann durch Hinzufügen einer Indirektionsebene * oder so etwas gelöst werden.
Führen Sie also ein nicht generisches Objekt ein, das eine höhere Ebene aufweist als a
Map
. Ohne Kontext wird es nicht sehr überzeugend aussehen, aber trotzdem:* Außer zu vielen Indirektionsebenen.
quelle
Hier ist eine Möglichkeit, wie ich damit umgehen kann, wenn ich den
equals()
Vorgang überschreibe .Dies scheint in Java 8 zu funktionieren (sogar kompiliert mit
-Xlint:unchecked
)quelle
Wenn Sie sicher sind, dass der von session.getAttribute () zurückgegebene Typ HashMap ist, können Sie nicht genau auf diesen Typ typisieren, sondern nur die generische HashMap überprüfen
Eclipse wird dann Warnungen überraschen, aber dies kann natürlich zu Laufzeitfehlern führen, die schwer zu debuggen sind. Ich verwende diesen Ansatz nicht nur in betriebskritischen Kontexten.
quelle
Zwei Möglichkeiten, eine, die das Tag vollständig vermeidet, die andere mit einer frechen, aber netten Utility-Methode.
Das Problem sind vorgenerisierte Sammlungen ...
Ich glaube, die Faustregel lautet: "Objekte einzeln umwandeln " - was dies bedeutet, wenn Sie versuchen, Rohklassen in einer generisierten Welt zu verwenden, ist, dass Sie nicht wissen, was Befindet sich in dieser Karte <?,?> (und tatsächlich stellt die JVM möglicherweise sogar fest, dass es sich nicht einmal um eine Karte handelt!), ist es offensichtlich, wenn Sie darüber nachdenken, dass Sie sie nicht wirken können. Wenn Sie eine Map <String,?> Map2 hatten, gibt HashSet <String> keys = (HashSet <String>) map2.keySet () keine Warnung aus, obwohl dies ein "Glaubensakt" für den Compiler ist (weil es könnte sich als TreeSet herausstellen) ... aber es ist nur ein einziges Akt des Glaubens.
PS zu dem Einwand, dass das Iterieren wie auf meine erste Weise "langweilig" ist und "Zeit braucht", lautet die Antwort "kein Schmerz, kein Gewinn": Eine generierte Sammlung enthält garantiert Map.Entry <String, String> s und nichts sonst. Sie müssen für diese Garantie bezahlen. Bei der systematischen Verwendung von Generika erfolgt diese Zahlung in Form der Codierungskonformität und nicht in Form von Maschinenzeit!
Eine Denkrichtung könnte sagen, dass Sie die Einstellungen von Eclipse so einstellen sollten, dass solche ungeprüften Casts-Fehler statt Warnungen auftreten. In diesem Fall müssten Sie meinen ersten Weg benutzen.
quelle
Dadurch verschwinden die Warnungen ...
quelle
Lösung: Deaktivieren Sie diese Warnung in Eclipse. Nicht @SuppressWarnings, sondern nur vollständig deaktivieren.
Einige der oben vorgestellten "Lösungen" sind weit von der Linie entfernt und machen Code unlesbar, um eine dumme Warnung zu unterdrücken.
quelle
@SuppressWarnings
macht den Code überhaupt nicht unlesbar.