2012-10-29 15 views
5

Quando si impiegano un approccio fluente si potrebbe avere il seguente scenario:Java - Ereditato perfetto tipo di metodo per restituire classe incidente il tipo, non i genitori

public class Foo<T extends a>{ 

    public Foo<T> someMethod(){ 
      System.out.println("foo"); 
      return this; 
    } 

} 


public class Bar<T extends b> extends Foo<T> { 

     public Bar<T> barMethod(){ 

      System.out.println("bar"); 
      return this; 
     } 

} 


public class a{} 
public class b extends a{} 

Un punto di forza di un'interfaccia fluida è che si può catena chiamate di metodo, ma mentre bar ha ereditato someMethod(), il tipo di ritorno è Foo non bar che avrebbe rotto la seguente catena:

new Bar<b>().someMethod().barMethod(); 

una risposta potrebbe essere quella di aggiungere @Overrides per ogni metodo ereditato, in modo tale che ha Bar il metodo aggiuntivo:

@Override 
public Bar<T> someMethod(){ 
      System.out.println("foo"); 
      return this; 
    } 

Ma nelle classi grandi e nelle gerarchie di classi estese questo può sicuramente essere un casino di ridondanza ?! Esiste un tipo di ritorno appropriato da fornire a metodi fluenti in base al quale ogni classe che eredita restituirà un oggetto del suo tipo specifico (in modo che possiamo concatenare metodi senza calchi)?

ho provato:

 public <U extends Foo<T>> U someMethod(){ 
      System.out.println("foo"); 
      return this; 
    } 

Per ahimè, senza alcun risultato. Spero che qualcuno conosca una soluzione semplice ed elegante a questo problema. Il progetto è ampio e quindi deve essere mantenibile ed estendibile se possibile.

Grazie a tutto l'aiuto che puoi fornire in questo scenario.

risposta

5

Si può fare in questo modo, se non ti dispiace un cast incontrollato:

public class Foo<T, F extends Foo<T, F>> { 
    public F someMethod() { 
    System.out.println("foo"); 
    return (F) this; // <--- That's the unchecked cast! Tucked safely away. 
    } 
    public static void main(String[] args) { 
    new Bar<String>().someMethod().barMethod(); 
    } 
} 

class Bar<T> extends Foo<T, Bar<T>> { 
    public Bar<T> barMethod() { 
    System.out.println("bar"); 
    return this; 
    } 
} 

Personalmente, a disattivare il cast incontrollato avviso globale per l'intero progetto.

+0

Ma c'è ancora un 'casting', che OP non vuole. Non so perché. Umm. Ma questo casting è migliore. È in un unico posto. Quindi, OP dovrebbe essere ok con esso. Altrimenti, Signore, aiutalo. ;) –

+1

@RohitJain vedere il commento che ho fatto sopra la risposta: È una cosa di usabilità, cercando di renderlo una lib molto semplice da usare per un client da includere nel loro progetto. La soluzione di Marko Topolnik è comunque interessante e qualcosa che non avevo considerato. Sembra che lanci di qualche descrizione siano inevitabili? – ComethTheNerd

+0

@MarkoTopolnik. Sì, ecco perché alla fine del mio commento, ho aggiunto che è un casting migliore, in un unico posto.:) –

1

Quello che puoi fare è definire il metodo barMethod in modo che ora puoi chiamarlo sul tipo Foo.

public abstract class Foo<T extends a> { 

    public Foo<T> someMethod() { 
     System.out.println("foo"); 
     return this; 
    } 

    public abstract Foo<T> barMethod(); 
} 

Ora si può facilmente chiamare

new Bar<b>().someMethod().barMethod(); 
+0

Il problema con questo approccio è che ci impedisce di istanziare direttamente Foo, che devo essere in grado di fare ancora. – ComethTheNerd

+0

@HodeCode. Quindi non vuoi aggiungere il metodo astratto, non vuoi sovrascrivere e non vuoi digitare. Hmmm. Sarà dura, se c'è un modo. Vediamo se qualcuno può trovare una soluzione. Ma non penso che ci sia una soluzione alternativa, se si ritagliano tutti i workaround. ma ancora non posso esserne sicuro. –

+0

Quindi dovrai lanciarlo. –

Problemi correlati