Método encadenamiento + herencia ¿no juegan bien juntos?
Esta pregunta se ha hecho en un contexto de C++ pero tengo curiosidad por Java. Las preocupaciones sobre los métodos virtuales no se aplican (creo), pero si usted tiene esta situación:
abstract class Pet
{
private String name;
public Pet setName(String name) { this.name = name; return this; }
}
class Cat extends Pet
{
public Cat catchMice() {
System.out.println("I caught a mouse!");
return this;
}
}
class Dog extends Pet
{
public Dog catchFrisbee() {
System.out.println("I caught a frisbee!");
return this;
}
}
class Bird extends Pet
{
public Bird layEgg() {
...
return this;
}
}
{
Cat c = new Cat();
c.setName("Morris").catchMice(); // error! setName returns Pet, not Cat
Dog d = new Dog();
d.setName("Snoopy").catchFrisbee(); // error! setName returns Pet, not Dog
Bird b = new Bird();
b.setName("Tweety").layEgg(); // error! setName returns Pet, not Bird
}
En este tipo de jerarquía de clases, ¿hay alguna manera de devolver this
de una manera que (efectivamente) no difunda el tipo de objeto?
5 answers
Si quieres evitar las advertencias de cast sin marcar de tu compilador (y no quieres @SuppressWarnings ("sin marcar")), entonces necesitas hacer un poco más:
En primer lugar, su definición de Pet debe ser autorreferencial, porque Pet es siempre un tipo genérico:
abstract class Pet <T extends Pet<T>>
En segundo lugar, el reparto (T) this
en Nombre_set está también desmarcado. Para evitar esto, utilice la técnica" getThis " en el excelente Generics FAQ de Angelika Langer :
El truco "getThis" proporciona un camino a recuperar el tipo exacto de este referencia.
Esto resulta en el siguiente código, que compila y se ejecuta sin advertencias. Si quieres extender tus subclases, entonces la técnica todavía se mantiene (aunque probablemente necesitarás generalizar tus clases intermedias).
El código resultante es:
public class TestClass {
static abstract class Pet <T extends Pet<T>> {
private String name;
protected abstract T getThis();
public T setName(String name) {
this.name = name;
return getThis(); }
}
static class Cat extends Pet<Cat> {
@Override protected Cat getThis() { return this; }
public Cat catchMice() {
System.out.println("I caught a mouse!");
return getThis();
}
}
static class Dog extends Pet<Dog> {
@Override protected Dog getThis() { return this; }
public Dog catchFrisbee() {
System.out.println("I caught a frisbee!");
return getThis();
}
}
public static void main(String[] args) {
Cat c = new Cat();
c.setName("Morris").catchMice();
Dog d = new Dog();
d.setName("Snoopy").catchFrisbee();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-01 18:07:38
¿Qué tal este viejo truco:
abstract class Pet<T extends Pet>
{
private String name;
public T setName(String name) { this.name = name; return (T) this; }
}
class Cat extends Pet<Cat>
{
/* ... */
}
class Dog extends Pet<Dog>
{
/* ... */
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-01 14:55:46
No, en realidad no. Podría solucionarlo usando tipos de retorno covariantes (gracias a McDowell por el nombre correcto):
@Override
public Cat setName(String name) {
super.setName(name);
return this;
}
(Los tipos de retorno covariantes solo están en Java 5 y superior, si eso es una preocupación para usted.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-24 16:48:53
Es un poco complicado, pero puedes hacer esto con genéricos:
abstract class Pet< T extends Pet > {
private String name;
public T setName( String name ) {
this.name = name;
return (T)this;
}
public static class Cat extends Pet< Cat > {
public Cat catchMice() {
System.out.println( "I caught a mouse!" );
return this;
}
}
public static class Dog extends Pet< Dog > {
public Dog catchFrisbee() {
System.out.println( "I caught a frisbee!" );
return this;
}
}
public static void main (String[] args){
Cat c = new Cat();
c.setName( "Morris" ).catchMice(); // error! setName returns Pet, not Cat
Dog d = new Dog();
d.setName( "Snoopy" ).catchFrisbee(); // error! setName returns Pet, not Dog
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-15 16:50:07
public class Pet<AnimalType extends Pet> {
private String name;
public AnimalType setName(String name) {
this.name = name; return (AnimalType)this;
}
}
Y
public class Cat extends Pet<Cat> {
public Cat catchMice() {return this;}
public static void main(String[] args) {
Cat c = new Cat().setName("bob").catchMice();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-15 16:50:20