2016-03-25 13 views
5

Der folgende Code stammt aus einer Android-Bibliothek namens ButterKnife. Ich finde heraus, wie es funktioniert.Java-Generics basierend auf Rückgabetyp?

@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); 
    } 
    } 

Ich habe versucht, das Verhalten dieser Funktion neu zu erstellen:

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

Und Nutzung:

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

Aber die Ausnahme noch nie gefangen, es an der Linie geworfen wird, wenn die Methode wird aufgerufen. Warum das?

Auch, wie funktioniert das genau? Woher weiß die Methode, auf was sie wirken soll?

Antwort

2

Wie SJuan67 erklärt, kann man nicht wirklich nutzen wirft mit generischen Typen als Java-Compiler

alle Parameter des Typs ersetzen in generische Typen mit ihren Grenzen oder Objekt, wenn die Parameter des Typs unbegrenzt sind. Der erzeugte Bytecode enthält daher nur gewöhnliche Klassen, Schnittstellen und Methoden.

Weitere Informationen zu allen Generika-Einschränkungen here.

So butter Code wird wie folgt aussehen:

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

So auf Ihre Fragen:

F: Aber die Ausnahme noch nie gefangen, es an der Linie geworfen wird, wenn die Methode aufgerufen wird. Warum das?

A: Nun, es ist nicht einmal im Bytecode.

Frage: Wie funktioniert das genau? Woher weiß die Methode, auf was sie wirken soll?

A: Tut es nicht. Zumindest nicht wie du denkst. In der Praxis wird es ClassCastException nicht IllegalStateException oder AssertionError werfen, wie Sie beobachtet haben. Sie können sogar mit Buttermesser Beispielanwendung versuchen es und eine bekannte Textview zu CheckBox binden:

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

F: Wie funktioniert dann die Bibliotheksarbeit?

A: Nun IllegalStateException wird nur nie aufgerufen und Sie haben ClassCastException. Warum es so ist, bin ich mir nicht wirklich sicher. Da ButterKnife jedoch Code generiert, könnte dies verhindern, dass Kompilierungsfehler auftreten.

zum Beispiel:

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 
} 

Welche warum im vorherigen Beispiel-Code ist kompiliert, aber nicht ausgeführt.

7

Generiktypen werden nur zum Zeitpunkt der Kompilierung geprüft, da sie gelöscht werden. Dies geschieht, weil es keine Möglichkeit gab, Generics in der Laufzeitumgebung in Java 5 einzuführen, ohne die Abwärtskompatibilität zu zerstören und alle bereits existierenden Bibliotheken neu zu kompilieren.

Lange Geschichte kurz, wenn Sie eine "generische" Klasse oder Methode definieren, wird der eigentliche Code als Object anstelle des Typs kompiliert, die Sie die Methode binden. Alle Prüfungen von Typen werden zur Kompilierzeit durchgeführt.

Ihr Methodencode führt also in der return-Anweisung keine Umwandlung aus, da er einem Object Rückgabewert einen Wert (String) zuweist. Die tatsächliche ClassCastException wird von der aufrufenden Zeile zurückgegeben, da dies der Ort ist, an dem die Referenzvariable tatsächlich eingegeben wird.

+0

Hier jemand anderes versucht, genau das gleiche zu tun: http://stackoverflow.com/questions/14524751/cast-object-to-generic-type-for-returning – SJuan76

+0

Wie funktioniert die Bibliothek dann? – Greyshack