2016-03-25 26 views
5

Il seguente codice proviene da una libreria Android denominata ButterKnife. Sto scoprendo come funziona.Cast di generici Java in base al tipo restituito?

@SuppressWarnings("unchecked") // That's the point. 
    public <T> T castParam(Object value, String from, int fromPosition, String to, int toPosition) { 
    try { 
     return (T) value; 
    } catch (ClassCastException e) { 
     throw new IllegalStateException("Parameter #" 
      + (fromPosition + 1) 
      + " of method '" 
      + from 
      + "' was of the wrong type for parameter #" 
      + (toPosition + 1) 
      + " of method '" 
      + to 
      + "'. See cause for more info.", e); 
    } 
    } 

ho cercato di ricreare il comportamento di questa funzione:

@SuppressWarnings("unchecked") 
    public static <T> T cast(Object o){ 
     try { 
      return (T) o; 
     } catch (ClassCastException e){ 
      throw new AssertionError("Error"); 
     } 
    } 

e di utilizzo:

Object o = new String("test"); 
Double d = cast(o); 

Ma l'eccezione non viene mai preso, ma si butta in linea quando il il metodo è chiamato. Perché?

Inoltre, come funziona esattamente? In che modo il metodo sa a cosa lanciare?

risposta

2

Come spiegato SJuan67, non si può davvero utilizzare getta con i tipi generici come il compilatore Java

sostituire tutti i parametri di tipo in tipi generici con i loro limiti o di un oggetto se i parametri di tipo sono illimitati. Il codice prodotto, quindi, contiene solo classi, interfacce e metodi ordinari.

Ulteriori informazioni su tutte le restrizioni sui generici here.

codice in modo butterknife sarà simile a questa:

public Object castParam(Object paramObject, String paramString1, int paramInt1, String paramString2, int paramInt2) 
    { 
    return paramObject; 
    } 

Così alle vostre domande:

D: Ma l'eccezione non viene mai preso, ma si butta in linea quando il metodo viene chiamato. Perché?

A: Beh, non è nemmeno nel bytecode.

Q: Inoltre, come funziona esattamente? In che modo il metodo sa a cosa lanciare?

A: Non è così. Almeno non come pensi che lo farà. In pratica getterà ClassCastException non IllegalStateException o AssertionError come hai osservato. Potete anche provare con il campione butterknife app e Associare un TextView noto per CheckBox:

@Bind(R.id.title) CheckBox title; 

Q: Come funziona il lavoro in biblioteca, allora?

A: Well IllegalStateException non viene mai chiamato e si dispone di ClassCastException. Perché è così che non sono davvero sicuro. Tuttavia, poiché ButterKnife genera codice, questo potrebbe essere inteso a prevenire errori di compilazione.

ad esempio:

public interface Some { 
} 

public static void weWantSome(Some d) { 
} 

public static void test() { 
    String o = "test"; 
    weWantSome((Some)o); //<-- compile error 
    weWantSome(Main.<Some>cast(o)); //<-- runtime error 
} 

È per questo che nel codice di esempio precedente compila, ma non funziona.

7

I tipi generici vengono controllati solo in fase di compilazione, a causa della cancellazione del tipo. Questo perché non è stato possibile introdurre i generici in runtime in Java 5 senza interrompere la compatibilità con le versioni precedenti e forzare la ricompilazione di tutte le librerie già esistenti.

Breve cronologia breve, quando si definisce una classe o un metodo "generico", il codice effettivo viene compilato come Object anziché il tipo a cui si sta vincolando il metodo. Tutti i controlli dei tipi vengono eseguiti in fase di compilazione.

Pertanto, il codice metodo non sta eseguendo un cast nell'istruzione return, poiché assegna qualcosa (a String) a un valore di ritorno Object. La ClassCastException effettiva viene restituita dalla linea chiamante perché è il luogo in cui la variabile di riferimento è effettivamente digitata.

+0

Qui qualcun altro ha cercato di fare esattamente la stessa cosa: http://stackoverflow.com/questions/14524751/cast-object-to-generic-type-for-returning – SJuan76

+0

Come funziona allora la libreria? – Greyshack