2016-07-19 14 views
11

Ich verstehe Generika und Casting, aber ich verstehe nicht, generische Casting. Ich dachte, ich nur auf bestimmte Art nach oben geworfen oder unten durch inheritence Baum aber dies erwies sich als mich nicht falsch:Casting auf generische Variable

ArrayList<?> cislo = (ArrayList<? extends Number>) new ArrayList<Integer>(); 

Dies könnte nicht das beste Beispiel sein, aber hoffentlich werden Sie mein Punkt. Wie funktioniert das? Zu welchem ​​Typ wird es gegossen?

+1

Zur Laufzeit gibt es keine Umwandlung, da die generischen Informationen verloren gehen (Typ löschen). Zur Kompilierzeit sagen Sie dem Compiler, dass er 'ArrayList ' als 'ArrayList 'und sei still darüber (außer der Warnung, im Grunde ist es das gleiche, als würdest du eine' Number' Variable in 'Integer' umwandeln - du sagst dem Compiler, dass du weißt was du tust). Die Zuweisung zu 'ArrayList ' funktioniert in jedem Fall. – Thomas

+0

Mit Generics sollte Gießen überhaupt nicht notwendig sein. Wenn Sie einen generischen Typ verwenden möchten, sollten Sie vielleicht prüfen, ob es keine "saubere" Lösung ohne Cast gibt, indem Sie einfach die enorme Flexibilität nutzen, die Generics haben. Ich bin sicher, dass viele Menschen bereit sind zu helfen, wenn Sie ein konkretes Beispiel haben. – martinhh

+0

Ich habe ein konkretes Beispiel. Ich arbeite mit framwork, das erlaubt, JUnit-Test zu kommentieren, um Speicher zu wählen, den ich Resultate an '' ReesmoConfiguration (Speicher = RestApiStorage.class) 'senden möchte und in der abstrakten Oberklasse Speicher fand ich Fabrikmethode ' newInstance (Gegenstandkonfiguration) { Klasse clazz = null; if (Bool.FALSE.equals (Property.ENABLED.get (Konfiguration))) { clazz = DummyStorage.class; } else { clazz = (Klasse ) Property.STORAGE.get (Konfiguration); } if (clazz.isAssignableFrom (DummyStorage.class)) { Rückgabe von neuem DummyStorage(); }} ' –

Antwort

4

Die Besetzung ist nicht notwendig.

Die Zuordnungen

ArrayList<?> list = new ArrayList<Integer>(); 
ArrayList<? extends Number> list2 = new ArrayList<Integer>(); 

benötigen kein explizites Casting.

Wildcard-Typen sind „allgemeine“/„weniger spezifisch“ Typen als Betontypen und upper bounded wildcard types (wie ? extends Number) sind spezifischer als unbounded ones (?).

Weitere Informationen über die Beziehung zwischen Wildcard-Typen finden Sie in der Oracle Tutorial on Wildcards and Subtyping.

Der relevante Teil des JLS Diese Angabe ist 4.10.2. Subtyping among Class and Interface Types

Bei einem gattungsgemäßen Art Erklärung C (n> 0), die direkten geordneten Typen des parametrisierten Typ C, wobei Ti (1 ≤ i ≤ n) ein Typ ist, werden alle der folgenden:

  • D, wobei D ein generischer Typ ist, der eine direkte Übertyp der gattungsgemäßen Art C und θ ist, ist die Substitution [F1: = T1, ..., Fn : = Tn].
  • C, wobei Si Ti enthält (1 ≤ i ≤ n) (§4.5.1).

[...]

die 4.5.1. Type Arguments of Parameterized Types

Typargument T1 genannten bezieht sich eine andere Art Argument T2 enthalten, geschrieben T2 < = T1, wenn der eingestellte der mit T2 bezeichneten Typen ist nachweisbar eine Teilmenge der mit T1 bezeichneten Arten von Typen unter dem reflexiven und transitiven Schließen der folgenden Regeln (wobei <: Subtyping (§4.10) bedeutet):

  • ? erweitert T < =? verlängert S wenn T <: S

  • ? erweitert T < =?

[...]

So nach dieser Definition ArrayList<? extends Number> ist ein übergeordneter Typ von ArrayList<Integer> und ArrayList<?> ist ein übergeordneter Typ eines ArrayList<> mit Ausnahme der rohen Art.

Zuordnung zu einer Variablen eines Typs, der ein Supertype ist, erfordert kein Casting.


Ein Abguss ist notwendig, wenn Sie etwas von einem anderen Typ zuweisen möchten:

Object list = new ArrayList<Integer>(); 
//...somewhere else - the compiler does not know that list is always an ArrayList, but we tell it that we know what we are doing 
List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning, but works 

Nach dieser Besetzung können Sie zum Beispiel Holen Sie sich Elemente aus der Liste und behandeln Sie sie als Number s. Die Besetzung wird zur Laufzeit fehlschlagen, wenn Sie etwas anderes zu der list Referenz zuweisen, das nicht ein Subtyp von List<? extends Number>

Object list = new HashSet<Integer>(); 
    List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning 

nicht zur Laufzeit werfen

java.lang.ClassCastException: java.util.HashSet cannot be cast to java.util.List


Die Probleme beginnen zu entstehen wenn der generische Typ nicht übereinstimmt:

List<String> stringList = new ArrayList<String>(); 
stringList.add("hi"); 
Object list = stringList; 
List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning, but (sadly) works 

Number n = numbers.get(0); //fails: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number 

Dies geschieht, weil zur Laufzeit das Löschen der Listentypen übereinstimmt. Siehe auch the tutorial on erasure

+0

"Die Besetzung ist unnötig." - Wie gesagt, könnte nicht das beste Beispiel sein. Ich finde es sehr schwer zu lesen, aber deine Erklärung hat wirklich geholfen, thx. Zusätzliche Informationen finden Sie hier http://stackoverflow.com/questions/2776975/how-can-i-add-to-list-exends-number-data-structures –