2010-12-08 7 views
20

Warnung ich den folgenden Code haben:Java: Wie die Ungeprüfter Guss beheben

private HashMap<Class<?>, HashMap<Entity, ? extends Component>> m_componentStores; 

public <T extends Component> T getComponent(Entity e, Class<T> exampleClass) 
{ 
    HashMap<Entity, ? extends Component> store = m_componentStores.get(exampleClass); 

    T result = (T)store.get(e); 

    if (result == null) 
    { 
     throw new IllegalArgumentException("GET FAIL: "+e+" does not possess Component of class\nmissing: "+exampleClass); 
    } 

    return result; 
} 

Wenn ich kompilieren, es zeigt, dass T result = (T)store.get(e) einen ungeprüften Stich hat.

Type safety: Unchecked cast from capture#2-of ? extends Component to T 

Was fehlt mir, damit diese Warnung nicht angezeigt wird?

Antwort

32

Class.cast ist was du willst. Nun, Sie könnten darüber nachdenken, keine Reflexion zu verwenden.

Ändern Sie die Zeile:

T result = (T)store.get(e); 

zu:

T result = exampleClass.cast(store.get(e)); 
+0

+1, immer besser, die in der Classcast zu halten Bibliothekscode IMO. Es ist jedoch nicht unbedingt notwendig, wenn Sie sich selbst beweisen können, dass Ihre Bibliothek keinen Typfehler macht (d. H. 'SetComponent' arbeitet korrekt und symmetrisch). Dann wird eine Unterdrückung Warnungen tun. –

+3

@Mark Peters In den meisten Fällen, in denen Programmierer sich selbst überzeugen, sind sie typischerweise falsch. –

+1

Ich glaube nicht, dass das bei allen Bibliotheksdesignern stimmt, und wenn es so ist, sollten sie keine Bibliotheken schreiben. Es gibt Beispiele für ungeprüfte Umwandlungen in der API. 'Collections.emptyList()' kommt mir in den Sinn. –

14

schreiben @SuppressWarnings("unchecked") über dem Guss Aussage:

@SuppressWarnings("unchecked") 
T result = (T)store.get(e); 

Und eine Begründung hinzufügen, warum es sicher ist, das ignorieren Warnung.

+0

Es ist nicht sicher zu ignorieren. Nehmen wir ein einfaches Beispiel: 'abstract class Tier {public abstract void makeNoice(); } ',' class Cat erweitert Animal {} 'und' class Dog erweitert Animal {} '. "Hund" und "Katze" sind beide "Tier" (sicher sind sie). Jetzt: 'class SomeAnimalUtils {public static void makeNoise (Tierisches Tier) {Dog dog = (Hund) animal; dog.makeNoice(); }} '. Nehmen wir an, dass beide die Methode korrekt implementieren. Nun kann 'SomeAnimalUtils.makeNoise()' eine 'Katze' nehmen, weil sie das gesuchte' Tier' erweitert, aber nicht in 'Hund' umgewandelt werden kann und Sie die' ClassCastException' verfälschen. – Roland

+0

Nun, mein Beispiel ist hier dumm, aber es sollte zeigen, dass Sie in der Tat mit unkontrollierten Umwandlungen umgehen müssen. Um zu verhindern, dass Leute "Katze" sondern nur "Hund" geben (was eine super Klasse für andere wie "Terrier" oder "Husky" sein könnte). Aber wenn jemand unbeabsichtigte "Katze" in der JVM gibt, löst die Ausnahme aus. Das ist schlecht, weil es bedeutet, dass der Programmierer von 'SomeAnimalUtils' den Parameter nicht überprüft und die Ausnahme selbst ausgelöst hat. – Roland

+0

Sie müssen dies also vorab überprüfen. Fügen Sie dazu den folgenden Code vor der unsicheren/nicht aktivierten Umwandlung hinzu: 'if (null == animal) {Neue NullPointerException (" Parameter 'animal' ist null "); } else if (! (animal instanceof Dog)) {neue ClassCastException auslösen ("Parameter 'animal' =" + animal.toString() + "ist keine Instanz von Dog"); } '. Hier muss (muss) man sich auch um die Nullreferenzprüfung kümmern, die von der Java-Sprache als "objected" -Parameter übergeben wird (= Instanz von "Object" oder irgendeine Schnittstelle). Wenn Sie sich nicht um Null-Referenzen kümmern, riskieren Sie vielleicht eine berüchtigte "NPE" hier. – Roland

3

extends in Generika funktioniert nicht wirklich so. T! = ? extends Component obwohl T extends Component. Was Sie haben, ist in der Tat ein wildcard capture, es hat einen anderen Zweck.

Und ja Ihre Lösung ist nicht typsicher - es gibt keine Beziehung zwischen den beiden ? Marken ist:

private HashMap<Class<?>, HashMap<Entity, ? extends Component>> m_componentStores; 

So wird es legal eine Instanz einer Unterklasse von Component in dieser Struktur mit einigen zu setzen andere Klasse (nicht einmal eine Unterklasse von Component) als Schlüssel.

Denken Sie daran, dass generische Typen nur bei Kompilierung aufgelöst werden, so bei Laufzeitm_componentStores keine Möglichkeit zu wissen hat, was genaue Art des Wertes du da drin haben, außer, dass es extendsComponent.

So ist der Typ, den Sie von store.get(e) bekommen ist ... Component:

Component result = store.get(e); 

Wenn Sie Component-T werfen, der Compiler gibt eine Warnung aus, weil die Besetzung nicht statisch überprüft werden kann. Aber wenn Sie sich der Semantik Ihrer Datenstruktur sicher sind, können Sie die Warnung einfach unterdrücken.

@SuppressWarnings("unchecked") 
    T resultT = (T)result; 

PS: Sie brauchen nicht eine Wildcard zu erfassen, wird die folgende Arbeit genau das gleiche in Ihrem Fall:

private HashMap<Class<?>, HashMap<Entity, Component>> m_componentStores;