Il concatenamento del metodo + l’ereditarietà non giocano bene insieme?

Questa domanda è stata posta in un contesto C ++ ma sono curioso di sapere su Java. Le preoccupazioni sui metodi virtuali non si applicano (credo), ma se si dispone di questa situazione:

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 } 

In questa sorta di gerarchia di classi, esiste un modo per restituirlo in un modo che non faccia (efficacemente) il tipo di object?

Se vuoi evitare gli avvisi di cast non selezionati dal tuo compilatore (e non vuoi @SuppressWarnings (“deselezionata”)), devi fare ancora un po ‘di più:

Prima di tutto, la tua definizione di Animale domestico deve essere autoreferenziale, perché Pet è sempre un tipo generico:

 abstract class Pet > 

In secondo luogo, anche il (T) this cast di setName è deselezionato. Per evitare questo, usa la tecnica “getThis” nelle ottime FAQ generiche di Angelika Langer :

Il trucco “getThis” fornisce un modo per recuperare il tipo esatto di questo riferimento.

Ciò risulta nel seguente codice, che compila e viene eseguito senza avvisi. Se vuoi estendere le sottoclassi, la tecnica rimane valida (anche se probabilmente dovrai generalizzare le tue classi intermedie).

Il codice risultante è:

 public class TestClass { static abstract class Pet > { private String name; protected abstract T getThis(); public T setName(String name) { this.name = name; return getThis(); } } static class Cat extends Pet { @Override protected Cat getThis() { return this; } public Cat catchMice() { System.out.println("I caught a mouse!"); return getThis(); } } static class Dog extends Pet { @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(); } } 

Che ne dici di questo vecchio trucco:

 abstract class Pet { private String name; public T setName(String name) { this.name = name; return (T) this; } } class Cat extends Pet { /* ... */ } class Dog extends Pet { /* ... */ } 

No, non proprio. Si può aggirare usando i tipi di ritorno covarianti (grazie a McDowell per il nome corretto):

 @Override public Cat setName(String name) { super.setName(name); return this; } 

(I tipi di ritorno covarianti sono solo in Java 5 e versioni successive, se questo è un problema per te.)

È un po ‘contorto, ma puoi farlo con i generici:

 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 } } 
 public class Pet { private String name; public AnimalType setName(String name) { this.name = name; return (AnimalType)this; } } 

e

 public class Cat extends Pet { public Cat catchMice() {return this;} public static void main(String[] args) { Cat c = new Cat().setName("bob").catchMice(); } 

}