method from java inheritance method-chaining

from - java super method



Método de encadenamiento+herencia no funcionan bien juntos? (5)

¿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> { /* ... */ }

Esta pregunta se ha realizado en un contexto de C ++, pero tengo curiosidad acerca de Java. Las preocupaciones sobre los métodos virtuales no se aplican (creo), pero si 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 no (efectivamente) revalorice el tipo de objeto?


Es un poco intrincado, 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 } }


No en realidad no. Podría solucionarlo utilizando 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 le preocupa).


Si desea evitar las advertencias de conversión no verificadas de su compilador (y no desea @SuppressWarnings ("desmarcado")), entonces necesita hacer un poco más:

Antes que nada, tu 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 (T) this elenco en setName también está desmarcado. Para evitar esto, use la técnica "getThis" en las excelentes Preguntas frecuentes sobre genéricos de Angelika Langer :

El truco "getThis" proporciona una forma de recuperar el tipo exacto de esta referencia.

Esto da como resultado el siguiente código, que se compila y se ejecuta sin advertencias. Si desea extender sus subclases, entonces la técnica aún se mantiene (aunque probablemente necesite genéricos sus 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(); } }


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(); }

}