2013-04-08 12 views
25

sto studiando Java funzione generico e non sono sicuro come spiegare la terza linea nella main seguente metodo:Invocazione Java metodi generici

public class Example4 { 
    public static void main(final String[] args) { 
     System.out.println(Util.<String>compare("a", "b")); 
     System.out.println(Util.<String>compare(new String(""), new Long(1))); 
     System.out.println(Util.compare(new String(""), new Long(1))); 
    } 
} 

class Util { 
    public static <T> boolean compare(T t1, T t2) { 
     return t1.equals(t2); 
    } 
} 

La prima linea compila, corre, e ritorna (come previsto) false.

La seconda riga non viene compilata come previsto, poiché sto mescolando esplicitamente String e Long.

La terza linea compila, corre, e restituire false, ma non sono sicuro di capire come succede a lavorare: fa il compilatore/JVM istanzia il tipo di parametro T come Object? (Inoltre, ci sarebbe un modo per ottenere questo tipo dichiarato di T sono di runtime?)

Grazie.

risposta

9

La risposta sembra andare oltre le risposte di @Telthien e @newacct. Ero curioso di "vedere" per me la differenza tra:

System.out.println(Util.<String>compare("a", "b")); 

con tipizzazione esplicitamente, e:

System.out.println(Util.compare(new String(""), new Long(1))); 

con tipizzazione implicita.

Ho eseguito diversi esperimenti, utilizzando le variazioni su queste due righe precedenti. Questi esperimenti dimostrano che, a meno di utilizzare la anonymous/local class trick, il compilatore non controlla i tipi durante la compilazione ma i bytecode generati riferisce solo alle Object, anche nel caso della prima linea.

Il seguente pezzo di codice mostra che typecasts può essere eseguita in modo sicuro fino Object anche nel caso del tipo esplicitamente tesi <String>.

public final class Example44 { 
    public static void main(final String[] args) { 
     System.out.println(new Util44<String>().compare("a", "b")); 
     System.out.println(new Util44().compare(new String(""), new Long(1))); 
    } 
} 

final class Util44<T> { 
    private T aT; 
    public boolean compare(T t1, T t2) { 
     System.out.println(this.aT); 
     // I was expecting the second and third assignments to fail 
     // with the first invocation because T is explicitly a String 
     // and then to work with the second invocation because I use 
     // a raw type and the compiler must infer a common type for T. 
     // Actually, all these assignments succeed with both invocation. 
     this.aT = (T) new String("z"); 
     this.aT = (T) new Long(0); 
     this.aT = (T) new Object(); 
     return t1.equals(t2); 
    } 
} 

I bytecode del metodo main assomigliano:

// Method descriptor #15 ([Ljava/lang/String;)V 
    // Stack: 7, Locals: 1 
    public static void main(java.lang.String[] args); 
    0 getstatic java.lang.System.out : java.io.PrintStream [16] 
    3 new ca.polymtl.ptidej.generics.java.Util44 [22] 
    6 dup 
    7 invokespecial ca.polymtl.ptidej.generics.java.Util44() [24] 
    10 ldc <String "a"> [25] 
    12 ldc <String "b"> [27] 
    14 invokevirtual ca.polymtl.ptidej.generics.java.Util44.compare(java.lang.Object, java.lang.Object) : boolean [29] 
    17 invokevirtual java.io.PrintStream.println(boolean) : void [33] 
    20 getstatic java.lang.System.out : java.io.PrintStream [16] 
    23 new ca.polymtl.ptidej.generics.java.Util44 [22] 
    26 dup 
    27 invokespecial ca.polymtl.ptidej.generics.java.Util44() [24] 
    30 new java.lang.String [39] 
    33 dup 
    34 ldc <String ""> [41] 
    36 invokespecial java.lang.String(java.lang.String) [43] 
    39 new java.lang.Long [46] 
    42 dup 
    43 lconst_1 
    44 invokespecial java.lang.Long(long) [48] 
    47 invokevirtual ca.polymtl.ptidej.generics.java.Util44.compare(java.lang.Object, java.lang.Object) : boolean [29] 
    50 invokevirtual java.io.PrintStream.println(boolean) : void [33] 
    53 return 
     Line numbers: 
     [pc: 0, line: 24] 
     [pc: 20, line: 25] 
     [pc: 53, line: 26] 
     Local variable table: 
     [pc: 0, pc: 54] local: args index: 0 type: java.lang.String[] 

Si rende effettivamente senso che tutte le chiamate sono sempre metodi con Object come tipi di parametri formali, come spiegato in another question/answer. Per conludere, il compilatore usa sempre Object per i bytecode generati, non importa se esiste un argomento di tipo explicity (prima riga) o un argomento di tipo implicito ma che gli oggetti potrebbero avere una superclasse comune diversa da Object.

+1

Solo per notare questo qui, anche se ci si collega ad esso; il fenomeno si chiama "cancellazione di tipo". –

+0

Sì. Fondamentalmente, Java implementa i generici come typecast dietro le quinte. Ad esempio, un 'ArrayList ' considera internamente ogni elemento come 'Object', ma lo automaticamente lo restituisce a' String' quando lo si usa. –

20

Il tipo ereditato condiviso di String e Long è Object.

Quando si esegue questa funzione come Util.<String>compare(, il compilatore si aspetta di trovare due input di stringa e genera un errore quando non lo fa. Tuttavia, eseguendolo senza <String> si ottiene l'utilizzo del tipo ereditato condiviso più vicino, in questo caso Object.

Così, quando compare accetta t1 e t2, sono stati espressi come Object, e il codice funziona bene.

Per ottenere il tipo effettivo in fase di esecuzione si utilizza la stessa tecnica che si usa con qualsiasi altro oggetto: il getClass() che viene ereditato dalla classe Object.

+1

Anche per la seconda parte (metodo per ottenere il tipo dichiarato di T in fase di esecuzione) cercare "Reified Generics" su Google. Consultare gafter.blogspot.com/2006/11/reified-generics-for-java.html – prashant

+0

Il metodo getClass() applicato a, ad esempio, t1, restituirà String perché è il tipo di runtime, non è vero? –

+0

Mentre facevo altre ricerche, mi sono imbattuto in questo interessante post di blog: [super-tipo token] (http://gafter.blogspot.kr/2006/12/super-type-tokens.html). –

2

Sì, Object è una scelta per T che consentirà la compilazione. Concettualmente, il compilatore deduce un tipo per T. Ciò che deduce in particolare non ha importanza, a condizione che possa dedurre che lo del tipo funzionerà per T, quindi verrà compilato. Non importa quale sia il tipo inferito, poiché non ha alcun effetto sul codice compilato.