Il cast non è necessario.
Le assegnazioni
ArrayList<?> list = new ArrayList<Integer>();
ArrayList<? extends Number> list2 = new ArrayList<Integer>();
non richiedono alcuna cast esplicito.
tipi jolly sono "più generale"/tipi di "meno specifici" di tipi concreti, e upper bounded wildcard types (come ? extends Number
) siano più specifici unbounded ones (?
).
Ulteriori informazioni sulla relazione tra i caratteri jolly sono disponibili nello Oracle Tutorial on Wildcards and Subtyping.
La parte rilevante della JLS specificare questo è 4.10.2. Subtyping among Class and Interface Types
Dato un tipo generico dichiarazione C (n> 0), i supertipi diretti del tipo C parametrizzato, dove Ti (1 ≤ i ≤ n) è un tipo, sono tutte le caratteristiche seguenti:
- D, dove D è un tipo generico che è un supertipo diretta del tipo C e θ generico è la sostituzione [F1: = T1, ..., Fn : = Tn].
- C, dove Si contiene Ti (1 ≤ i ≤ n) (§4.5.1).
[...]
che si riferisce a 4.5.1. Type Arguments of Parameterized Types
Un tipo di argomento T1 si dice contenga un altro T2 tipo di argomento, T2 scritta < = T1, se il set dei tipi denotati da T2 è probabilmente un sottoinsieme dell'insieme di tipi denotato da T1 sotto la chiusura riflessiva e transitiva delle seguenti regole (dove <: denota sottotipizzazione (§4.10)):
[...]
Quindi, questa definizione ArrayList<? extends Number>
è un supertipo di ArrayList<Integer>
e ArrayList<?>
è un supertipo di qualsiasi ArrayList<>
ad eccezione del tipo grezzo.
L'assegnazione a una variabile di un tipo che è un supertipo non richiede il casting.
Un cast è necessario se si desidera assegnare una sorta di tipo diverso:
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
Dopo quel cast, è possibile per esempio ottenere elementi dalla lista e trattarli come Number
s. Il cast non riuscirà a runtime se si assegna qualcos'altro per il riferimento list
che non è un sottotipo di List<? extends Number>
Object list = new HashSet<Integer>();
List<? extends Number> numbers = (List<? extends Number>) list; //unchecked cast warning
non riesce a runtime gettando
java.lang.ClassCastException: java.util.HashSet cannot be cast to java.util.List
I problemi iniziano a sorgere quando il tipo generico non corrisponde:
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
Questo accade perché in fase di esecuzione, la cancellazione dei tipi di elenco corrisponde. Vedi anche the tutorial on erasure
In fase di esecuzione non è presente alcun cast, poiché le informazioni generiche vengono perse (cancellazione del tipo). Al momento della compilazione, dici al compilatore di gestire 'ArrayList' come 'ArrayList estende Number> 'e sii tranquillo (tranne l'avviso, in pratica è come se avessi lanciato una variabile' Number' su 'Integer' - dici al compilatore che sai cosa stai facendo). L'assegnazione a 'ArrayList >' funziona in ogni caso. –
Thomas
Con Generics, la trasmissione non dovrebbe essere affatto necessaria. Se hai voglia di lanciare un tipo generico, forse dovresti controllare se non c'è una soluzione "pulita" senza cast, semplicemente usando l'enorme flessibilità che ha Generics. Sono sicuro che molte persone saranno d'aiuto se si dispone di un esempio specifico. – martinhh
Ho un esempio specifico. Lavoro con framwork che consente di annotare il test JUnit per selezionare lo spazio di archiviazione che voglio inviare ad es. '@ReesmoConfiguration (storage = RestApiStorage.class)' e nella superclasse astratta Storage ho trovato il metodo factory 'newInstance (Object configuration) { Class estende Storage> clazz = null; if (Bool.FALSE.equals (Property.ENABLED.get (configuration))) { clazz = DummyStorage.class; } else { clazz = (Classe Estende Archiviazione>) Property.STORAGE.get (configurazione); } if (clazz.isAssignableFrom (DummyStorage.class)) { return new DummyStorage(); }} –