2012-06-10 15 views
17

Estoy trabajando en un problema donde hay varias implementaciones de Foo, acompañadas de varias FooBuilder. Mientras que Foo comparten varias variables comunes que deben establecerse, también tienen variables distintas que requieren sus respectivos FooBuilder para implementar alguna funcionalidad específica. Para la concisión, me gustaría tener los FooBuilder 's emisores de utilizar el método de encadenamiento, como:Java: devolución de la subclase en el método de la superclase firma

public abstract class FooBuilder { 
    ... 

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

    ... 
} 

y

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; 
    } 
    ... 
} 

Y así sucesivamente, con varias implementaciones diferentes FooBuilder. Esto técnicamente hace todo lo que quiero, sin embargo, este enfoque es sensible al orden de las llamadas a métodos cuando se realiza el encadenamiento de métodos. El siguiente método tiene indefinido errores de compilación:

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

que requieren que el desarrollador piensa en el orden de las llamadas de método en la cadena. Para evitar esto, me gustaría que los setters en FooBuilder de alguna manera devuelvan la subclase de implementación real. Sin embargo, no estoy seguro de cómo hacer esto. ¿Cuál es el mejor enfoque?

+0

Además, fuerza fundición en todas partes, o le impide cambiar propiedades de superclases cuando lo desee. –

Respuesta

13

Esta es una buena pregunta y un problema real.

La forma más fácil de manejarlo en Java probablemente implica el uso de genéricos, como se menciona en la respuesta de Jochen.

Hay una buena discusión del problema y una solución razonable en esta entrada de blog en Using Inheritance with Fluent Interfaces, que combina los genéricos con la definición de un método getThis() anulado en las subclases constructor para resolver el problema de siempre volviendo un constructor de la clase correcta.

+1

Me gusta esta solución. Mucho más elaborado de lo que propuse, pero también mucho más flexible y, en definitiva, elegante. – Jochen

+0

Solo para resumir el blog vinculado y eliminar los constructores por separado: 'clase abstracta pública X > {public int a; public B setA (int foo) {this.a = foo; return getThis();} resumen público B getThis();} public class Y extends X {public int b; public Y setB (int bar) {this.b = bar; return getThis();} public Y getThis() {return this;}} ' –

3

Los genéricos pueden ser el camino a seguir aquí.

Si se declara Seta() algo como esto (pseudo-código)

<T> T setA(int a) 

el compilador debe ser capaz de averiguar los tipos reales, y si no se le puede dar consejos en el código como

obj.<RealFoo>setA(42) 
+3

+1: Hay una buena descripción de esta táctica en http://egalluzzo.blogspot.com/2010/06/using-inheritance-with-fluent.html –

+0

El compilador * no * es capaz de determinar el tipo de encadenado invocación de método, y repetir el tipo para cada invocación de método no es una opción. – meriton

+0

@DonRoby: ¿Por qué no publica eso como respuesta? Resulta ser una mejor solución que la de Jochen y seguramente merecería un voto positivo ;-) – meriton

1

Encontré this excellent answer Ahora lo estoy compartiendo.

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 
Cuestiones relacionadas