2014-05-20 16 views
7

Während ich mit Lösungen für this question herumspielte, kam ich auf den folgenden Code, der einige Compilerwarnungen enthält. Eine Warnung ist:Generische Methode löst Sicherheitsfehler aus - warum?

Type safety: The expression of type Test.EntityCollection needs unchecked conversion to conform to Test.EntityCollection<Test.Entity>

ich nicht ganz verstehen, warum diese Warnung wird angezeigt. Wenn ich einen Class<M> Typ übergebe und deklariere, dass die Methode EntityCollection<M> zurückgibt, warum tue ich nicht genug, um den (Java 7) Compiler davon zu überzeugen, dass der korrekte Typ zurückgegeben wird?

static class Entity { 
} 

static class EntityCollection<E extends Entity> { 

    private EntityCollection(HashMap<?, E> map) { 
    } 

    public static <T extends HashMap<?, M>, M extends Entity> EntityCollection<M> getInstance(
      Class<T> mapType, Class<M> entityType) 
      throws ReflectiveOperationException { 

     T map = mapType.getConstructor().newInstance(); 
     return new EntityCollection<M>(map); 
    } 
} 

public static void main(String[] args) throws Exception { 
    // both compiler warnings are on the line below: 
    EntityCollection<Entity> collection = EntityCollection.getInstance(
      LinkedHashMap.class, Entity.class); 
} 

Bonuspunkte, wenn jemand den Code verbessern kann, um Warnungen vollständig zu vermeiden. Ich habe es eine Weile lang angeguckt und habe mir keine Möglichkeit ausgedacht, die Warnungen zu verringern.

+0

Hmm, mit ein wenig mehr Experimente kann ich nicht bekommen entweder Java 7 oder Java 8, um dagegen zu protestieren (außer einem "Sie könnten Diamantschlussfolgerung verwenden" auf dem 'return new EntityCollection (Karte)'). Ansonsten ist es sehr glücklich. Ich benutze Netbeans –

+0

@RichardTingle Ich kann Java 1.8.0_05 bekommen, um mir Warnungen zu geben, wenn ich mit 'javac -Xlint: alle' in der Befehlszeile kompiliere. –

Antwort

3

Das Problem ist, dass getInstance eine generische Methode ist, aber Sie nicht generische Typparameter daran übergeben. Sie können es umgehen, indem sie wie folgt übergeben:

public static void main(String[] args) throws Exception { 
     EntityCollection<Entity> collection = EntityCollection.<LinkedHashMap, Entity>getInstance(
       LinkedHashMap.class, Entity.class); 
    } 

Sie werden noch ein rawtypes beschäftigen müssen warnen, weil LinkedHashMap ein generischer Typ ist. Dies ist in Ihrem Fall problematisch, da in dem Schlüssel der Schlüssel ein Platzhalter vorhanden ist.

Sie stehen mehrere Probleme hier:

Sie können nicht parametrisierte Klassenobjekte wie diese passieren: LinkedHashMap<Object, Entity>.class so ziemlich Sie stecken mit den rawtypes warnen.

+0

Danke für diese Erklärung. Beachten Sie, dass mein 'Class entityType' Argument redundant ist und ich könnte dies gerne entfernen, nachdem Sie Ihre Änderungen vorgenommen haben. –

+0

Ihr Hauptproblem besteht also darin, dass jeder Aufruf dieser Methode eine Warnung erzeugt und Sie diese unterdrücken möchten, wenn Sie sie in die Methode verschieben? –

+0

Meistens war ich daran interessiert zu verstehen, warum diese spezielle Warnung empfangen wurde, daher haben Sie meine akzeptierte Antwort. Claudio's Antwort könnte sich auf lange Sicht als nützlicher erweisen, da die Warnungen leichter unterdrückt werden können. –

1

Das Problem dort ist T. Sie fügen eine Einschränkung zu Ihrer Methode, die besagt, dass T erweitert werden soll HashMap<?, M>. Die Art, wie Sie später auf T referenzieren, ist jedoch wie ein generischer Parameter vom Typ Class (Class<T>). LinkedHashMap.class ist vom Typ Class<LinkedHashMap> nicht Class<LinkedHashmap<?, Entity>> (was Sie brauchen)

Ein Klassenobjekt verweist immer auf einen nicht parametrisierten Typ, und das ist sinnvoll. Da die generische Bindung in der Kompilierungszeit vorhanden ist, verwenden Sie diese Klasse, um den Status und das Verhalten einer Instanz in Runtime dynamisch widerzuspiegeln. Lange Rede, kurzer Sinn: Sie können eine Class<HashMap> verwenden, um eine neue Instanz zu erstellen, die nicht an einen Typ gebunden ist.

Also, ich denke, was Sie Ihren Code zu tun, müssen diese Einschränkung in der Weise zu ändern, wie es aussieht:

public static <T extends HashMap, M extends Entity> EntityCollection<M> getInstance(
      Class<T> mapType, Class<M> entityType) 
      throws ReflectiveOperationException { 

     T map = mapType.getConstructor().newInstance(); 
     return new EntityCollection<M>(map); 
} 
+0

Ich mag, wie dies die Warnungen in die Factory-Methode zieht (wo sie mit '@SuppressWarnings ({" rawtypes "," unchecked "})') unterdrückt werden können, anstatt sie dem Aufrufer zu enthüllen. –

+0

Ja, ich entferne Warnungen hier nicht vollständig. Generika sind in Java viel zu rigide und ich denke, dass sie in solchen Dingen untergehen. Aber die Frage ist, brauchen wir wirklich mehr? Das Einkapseln der Warnung bei der Factory-Methode ist gut genug und fügt keine zusätzliche Komplexität hinzu. – Claudio

Verwandte Themen