2010-07-19 10 views
11

Con una clase abstracta quiero definir un método que devuelve "este" de las subclases:devolver un objeto subclasifican con los genéricos

public abstract class Foo { 
    ... 
    public <T extends Foo> T eat(String eatCake) { 
     ... 
     return this; 
    } 
} 

public class CakeEater extends Foo {} 

Quiero ser capaz de hacer cosas como:

CakeEater phil = new CakeEater(); 
phil.eat("wacky cake").eat("chocolate cake").eat("banana bread"); 

Podría decirse que el pan de plátano arrojaría una IllegalArgumentException con el mensaje "¡No es un pastel!"

Respuesta

13
public abstract class Foo<T extends Foo<T>> // see ColinD's comment 
{ 
    public T eat(String eatCake) 
    { 
     return (T)this; 
    } 
} 

public class CakeEater extends Foo<CakeEater> 
{ 
    public void f(){} 
} 

Editar

No hay ningún problema para requerir comportan en una subclase de cierta manera que está más allá de lo que puede verificar el tipado estático. Hacemos eso todo el tiempo - páginas y páginas de inglés simple para especificar cómo se escribe una subclase.

La otra solución propuesta, con el tipo de retorno covariante, debe hacer lo mismo - pidiendo a los implementadores de la subclase, en inglés simple, que devuelva el tipo de this. Ese requisito no se puede especificar mediante tipado estático.

+1

Debería ser 'Foo >'. Esto funciona, aunque puedes crear fácilmente una clase que explote con una 'ClassCastException' cuando llamas' eat', como 'class Bar extends Foo '. – ColinD

+0

Esta es la solución que terminé usando, pero notará que la devolución tiene una advertencia de lanzamiento desactivada. Si bien, lógicamente, tiene sentido que podamos convertir a T ya que esta es una subclase de T. – Sarabjot

+2

Sí.En un mundo mejor, Java podría tener un tipo 'This', que sería muy útil en muchos casos. – irreputable

5

no creo que necesita genéricos de Java 5 (y posteriores) tiene tipos de retorno covariantes, por ejemplo:

public abstract class Foo { 
    ... 
    public Foo eat(String eatCake) { 
     ... 
     return this; 
    } 
} 

public class CakeEater extends Foo { 

    public CakeEater eat(String eatCake) { 
     return this; 
    } 
} 
+0

Si está completamente definido en la superclase, crea un código redundante para definirlo en la subclase; Podría llamar a la superclase, pero esto parece innecesario. Funciona si lo haces como tipo T y suprimes las advertencias. – Sarabjot

+0

Funciona si lo haces como tipo T y suprimes las advertencias. La razón debe ser de vuelta del tipo de la subclase se debe a que: methodTakesCakeEater ((nueva CakeEater) .eat ("torta") coma ("pie"); methodTakesCakeEater necesita un parámetro CakeEater – Sarabjot

+5

Si a.. solución contiene '@ SuppressWarnings' no se debe considerar una solución. Solo mis dos centavos. – whiskeysierra

15

El enfoque de buen gusto desde el punto de vista del cliente (que generalmente es el que desea tomar) es usar tipos de retorno covariantes que se agregaron a los genéricos de soporte, como señala Michael Barker.

El buen gusto un poco menos, pero más elegante que un molde es agregar un método getThis:

protected abstract T getThis(); 

public <T extends Foo> T eat(String eatCake) { 
    ... 
    return getThis(); 
} 
0

Un enfoque que he usado antes para lograr un comportamiento similar es tener la subclase pasar su tipo en una constructor del tipo principal (generizado). A modo de descargo de responsabilidad, estaba generando las subclases sobre la marcha y la herencia era un poco engañosa para mantener mi generación de código simple, ya que siempre mi primer instinto es tratar de eliminar por completo la relación de extensiones.

Cuestiones relacionadas