2012-06-10 10 views
17

Sto lavorando su un problema in cui sono presenti diverse implementazioni di Foo, accompagnate da diversi FooBuilder. Mentre la condivisione di Foo condivide molte variabili comuni che devono essere impostate, hanno anche variabili distinte che richiedono il loro rispettivo FooBuilder per implementare alcune funzionalità specifiche. Per brevità, mi piacerebbe avere la 'S setter FooBuilder usare il metodo concatenamento, come:Java: restituzione della sottoclasse in modalità superclasse firma

public abstract class FooBuilder { 
    ... 

    public FooBuilder setA(int A) { 
    this.A = A; 
    return this; 
    } 

    ... 
} 

e

public class FooImplBuilder extends FooBuilder{ 
    ... 
    public FooImplBuilder setB(int B) { 
    this.B = B; 
    return this; 
    } 
    public FooImplBuilder setC(int C) { 
    this.C = C; 
    return this; 
    } 
    ... 
} 

E così via, con diversi FooBuilder implementazioni. Questo tecnicamente fa tutto ciò che voglio, tuttavia, questo approccio è sensibile all'ordine delle chiamate dei metodi quando viene eseguito il concatenamento del metodo. Il metodo seguente è undefined errori di compilazione:

someFoo.setA(a).setB(b)... 

richiedono che lo sviluppatore pensare l'ordine delle chiamate di metodo nella catena. Per evitare ciò, mi piacerebbe che i setter in FooBuilder restituissero in qualche modo la sottoclasse di implementazione effettiva. Tuttavia, non sono sicuro di come farlo. Qual è l'approccio migliore?

+0

Inoltre, obbliga a lanciare ovunque, o impedisce di modificare le proprietà delle superclassi quando si desidera. –

risposta

13

Questa è una buona domanda e un problema reale.

Il modo più semplice per gestirlo in Java implica probabilmente l'uso di generici, come menzionato nella risposta di Jochen.

C'è una buona discussione del problema e una soluzione ragionevole in questo blog su Using Inheritance with Fluent Interfaces, che unisce generici con la definizione di un metodo getThis() sovrascritto in sottoclassi Builder per risolvere il problema di sempre restituire un costruttore della classe corretta.

+1

Mi piace questa soluzione. Molto più elaborato di quello che ho proposto, ma anche molto più flessibile e alla fine elegante. – Jochen

+0

Solo per riepilogare il blog collegato e rimuovere i builder separati: 'classe astratta pubblica X > {pubblico int a; pubblico B setA (int foo) {this.a = foo; return getThis();} public abstract B getThis();} public class Y estende X {public int b; public Y setB (int bar) {this.b = bar; return getThis();} public Y getThis() {return this;}} ' –

3

I generici potrebbero essere la strada da percorrere qui.

Se si dichiara Seta() qualcosa di simile (pseudo-codice)

<T> T setA(int a) 

il compilatore dovrebbe essere in grado di capire i tipi reali, e se non è possibile dare suggerimenti nel codice come

+3

+1: C'è una buona descrizione di questa tattica su http://egalluzzo.blogspot.com/2010/06/using-inheritance-with-fluent.html –

+0

Il compilatore * non * è in grado di capire il tipo in incatenato il richiamo del metodo e la ripetizione del tipo per ogni richiamo del metodo non sono certo un'opzione. – meriton

+0

@DonRoby: Perché non lo pubblichi come risposta? Capita di essere una soluzione migliore di quella di Jochen e meriterebbe sicuramente un upvote ;-) – meriton

1

Dopo aver trovato this excellent answer Ora lo sto condividendo.

public class SuperClass<I extends SuperClass> 
{ 
    @SuppressWarnings("unchecked") // If you're annoyed by Lint. 
    public I doStuff(Object withThings) 
    { 
     // Do stuff with things. 
     return (I)this ; // Will always cast to the subclass. Causes the Lint warning. 
    } 
} 

public class ImplementationOne 
extends SuperClass<ImplementationOne> 
{} // doStuff() will return an instance of ImplementationOne 

public class ImplementationTwo 
extends SuperClass<ImplementationTwo> 
{} // doStuff() will return an instance of ImplementationTwo