Method chaining + inheritance don’t play well together?

五迷三道 提交于 2019-11-26 11:44:57

If you want to avoid unchecked cast warnings from your compiler (and don't want to @SuppressWarnings("unchecked")), then you need to do a little more:

First of all, your definition of Pet must be self-referential, because Pet is always a generic type:

abstract class Pet <T extends Pet<T>>

Secondly, the (T) this cast in setName is also unchecked. To avoid this, use the "getThis" technique in the excellent Generics FAQ by Angelika Langer:

The "getThis" trick provides a way to recover the exact type of the this reference.

This results in the code below, which compiles and runs without warnings. If you want to extend your subclasses, then the technique still holds (though you'll probably need to genericise your intermediate classes).

The resulting code is:

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

How about this old 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>
{
    /* ... */
}
Michael Myers

No, not really. You could work around it by using covariant return types (thanks to McDowell for the correct name):

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

(Covariant return types are only in Java 5 and above, if that's a concern for you.)

Steve B.

It's a bit convoluted, but you can do this with generics:

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
    }

}
Paul Adamson
public class Pet<AnimalType extends Pet> {

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

and

public class Cat extends Pet<Cat> {

    public Cat catchMice() {return this;}

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

}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!