2016-07-19 209 views
11

Capisco generico e casting ma non capisco il casting generico. Ho pensato che posso lanciare solo al tipo specifico verso l'alto o verso il basso attraverso albero di ereditarietà, ma questo si è rivelato fraintendetemi:Trasmissione alla variabile generica

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

Questo potrebbe non essere l'esempio migliore, ma speriamo che si otterrà il mio punto. Come funziona? A che tipo si fondono?

+1

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 '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

+0

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

+0

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 clazz = null; if (Bool.FALSE.equals (Property.ENABLED.get (configuration))) { clazz = DummyStorage.class; } else { clazz = (Classe ) Property.STORAGE.get (configurazione); } if (clazz.isAssignableFrom (DummyStorage.class)) { return new DummyStorage(); }} –

risposta

4

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)):

  • ? estende T < =? si estende S se T <: S

  • ? estende T < =?

[...]

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

+0

"Il cast non è necessario." - come ho detto non potrebbe essere il miglior esempio. Trovo che jls sia davvero difficile da leggere, ma la tua spiegazione mi ha davvero aiutato, grazie. Ulteriori informazioni possono essere trovate qui http://stackoverflow.com/questions/2776975/how-can-i-add-to-list-extends-number-data-structures –