2014-12-06 10 views
8

Questo programma non fa ciò che volevo. Stampa "triste" due volte, mentre speravo che fosse stampato "felice" e poi "triste".Impossibile scegliere il metodo appropriato utilizzando Java Generics

public class Woof { 

    public static class Arf<T> { 
     T yap; 
     public Arf(T yap) { 
      this.yap = yap; 
     } 

     public String woof() { 
      /* 
      * Should select which doYapStuff() based on whether T 
      * happens to be an Integer, or something else. 
      */ 
      return doYapStuff(yap); 
     } 

     /* Special case implementation of doYapStuff() where T is Integer */ 
     public String doYapStuff(Integer x) { 
      return "happy"; 
     } 

     /* Default implementation of doYapStuff() where T is something else */ 
     public String doYapStuff(T x) { 
      return "sad"; 
     } 
    } 

    public static void main(String[] args) { 
     Integer i = 5; 
     Arf<Integer> arf1 = new Arf<Integer>(i); 
     System.out.println(arf1.woof()); // Should print "happy" 

     String s = "foo"; 
     Arf<String> arf2 = new Arf<String>(s); 
     System.out.println(arf2.woof()); // Should print "sad" 
    } 

} 

risposta

9

questo è spiegato in questo blog: http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ050

rilevanti citazione:

Come può accadere questo? Passiamo un argomento di tipo String al metodo sovraccarico e tuttavia viene chiamata la versione per il tipo Object. Il motivo è che il compilatore crea solo una rappresentazione di codice byte per tipo o metodo generico e mappa tutte le istanze del tipo o metodo generico a quella rappresentazione.

Nell'esempio il metodo generico viene traslata nella seguente rappresentazione:

(edit: ho cambiato il metodo per abbinare questa domanda)

public String doYapStuff(Object x) { 
      return "sad"; 
} 

Considerando questa traduzione, dovrebbe essere ovvio perché viene invocata la versione Object del metodo overload. È del tutto irrilevante quale tipo di oggetto viene passato al metodo generico e quindi passato al metodo sovraccarico. Osserveremo sempre una chiamata della versione Object del metodo overload.

Più in generale: la risoluzione del sovraccarico si verifica in fase di compilazione, ovvero il compilatore decide quale versione in sovraccarico deve essere richiamata. Il compilatore lo fa quando il metodo generico viene tradotto nella sua rappresentazione di byte code univoca. Durante quel tipo di traduzione viene eseguita la cancellazione, il che significa che i parametri di tipo vengono sostituiti dal loro limite più a sinistra o da Oggetto se non è stato specificato alcun limite. Di conseguenza, il limite più a sinistra o Object determina quale versione di un metodo sovraccarico viene richiamata. Quale tipo di oggetto viene passato al metodo in fase di esecuzione è del tutto irrilevante per la risoluzione del sovraccarico.

Si può vedere questo se si guarda al bytecode compilato:

4 getfield Woof$Arf.yap : java.lang.Object [16] 
7 invokevirtual java.io.PrintStream.println(java.lang.Object) : void [32] 
10 aload_0 [this] 
11 aload_0 [this] 
12 getfield Woof$Arf.yap : java.lang.Object [16] 
15 invokevirtual Woof$Arf.doYapStuff(java.lang.Object) : java.lang.String [37] 
18 areturn 

per ottenere quello che vuoi, probabilmente è necessario utilizzare il modello strategia.

public interface YapSound{ 
    String  doYapSound(); 
} 

public class HappySound implements YapSound{ 

    @Override 
    public String doYapSound() { 
      return "happy"; 
    } 

} 

public class SadSound implements YapSound{ 

    @Override 
    public String doYapSound() { 
     return "sad"; 
    } 

} 

public class Arf { 
    YapSound yap; 
    public Arf(YapSound yap) { 
     this.yap = yap; 
    } 

    public String woof() { 
     /* 
     * Should select which doYapStuff() based on whether T 
     * happens to be an Integer, or something else. 
     */ 
     return yap.doYapSound(); 
    } 


} 

public static void main(String[] args) { 

    Arf arf1 = new Arf(new HappySound()); 
    System.out.println(arf1.woof()); // Should print "happy" 

    Arf arf2 = new Arf(new SadSound()); 
    System.out.println(arf2.woof()); // Should print "sad" 
} 
1

Quando si crea un'istanza del generico con Integer per T, T sta andando sempre ignorare intero a causa del modo in cui Java implementa generici utilizzando la cancellazione di tipo.