Frage

Diese Frage in einem C ++ Zusammenhang wurde gefragt aber ich bin mal gespannt über Java. Die Bedenken über virtuelle Methoden gelten nicht (glaube ich), aber wenn Sie diese Situation haben:

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 dieser Art von Klassenhierarchie, ist es eine Möglichkeit, this in einer Art und Weise zurück, die nicht (effektiv) upCast die der Objekttyp?

War es hilfreich?

Lösung

Wenn Sie nicht markiert Guss Warnungen von Ihrem Compiler vermeiden wollen (und wollen nicht @SuppressWarnings ( „ungeprüft“)), dann müssen Sie ein wenig mehr tun:

Zu allererst Ihre Definition von Pet muss selbstbezogen sein, weil Pet ist immer ein generischer Typ:

abstract class Pet <T extends Pet<T>>

Zweitens ist der (T) this Guss in setName auch nicht markiert. Um dies zu vermeiden, verwenden Sie die "getThis" Technik in der ausgezeichneten Generics FAQ von Angelika Langer

  

Der „getThis“ Trick bietet eine Möglichkeit,   die genaue Art der diese erholen   Bezug genommen wird.

Dies führt zu dem folgenden Code, der ohne Warnungen und läuft kompiliert. Wenn Sie Ihre Unterklassen erweitern möchten, dann noch die Technik hält (obwohl Sie wahrscheinlich Ihre Zwischenklassen genericise müssen).

Der resultierende Code ist:

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

Andere Tipps

Wie dieser alten Trick:

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

Nein, nicht wirklich. Sie könnten durch die Verwendung kovarianten Rückgabetypen (dank McDowell für den richtigen Namen) um es:

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

(Covariant Rückgabetypen sind nur in Java 5 und höher, wenn das ein Problem für Sie.)

Es ist ein bisschen gewunden, aber Sie können dies mit Generika tun:

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<AnimalType extends Pet> {

private String name;
    public AnimalType setName(String name) {
       this.name = name; return (AnimalType)this; 
    }        
}

und

public class Cat extends Pet<Cat> {

    public Cat catchMice() {return this;}

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

}

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top