Подтвердить что ты не робот

OOP в Java: Наследование классов с помощью цепочки методов

У меня есть родительский класс, который определяет набор методов цепочек (методы, возвращающие "this"). Я хочу определить несколько дочерних классов, которые содержат свои собственные методы цепочки, но также "переопределяют" родительские методы, чтобы экземпляр дочернего класса возвращался вместо родительского класса.

Я не хочу повторять одни и те же методы в каждом дочернем классе, поэтому у меня есть родительский класс, содержащий методы, которыми обладают все дочерние классы. Спасибо.

class Chain {
  public Chain foo(String s){
    ...
    return this;
  }
}

class ChainChild extends Chain {
  //I don't want to add a "foo" method to each child class
  /*
  public ChildChain foo(String s){
    ...
    return this;
  }
  */

  public ChainChild bar(boolean b){
    ...
    return this;
  }
}

ChainChild child = new ChainChild();
child.foo().bar(); //compile error: foo() returns a "Chain" object which does not define the bar() method. 
4b9b3361

Ответ 1

Метод родительского класса, который возвращает this, все равно вернет ссылку на объект дочернего класса. Вы сможете обрабатывать его только как объект родительского класса (если вы его не набросаете), но он будет действительно оригинальным.

Вы можете использовать дженерики следующим образом:

// This seems a bit too contrived for my liking. Perhaps someone else will have a better idea.
public class Parent<T extends Parent<T>> {
    T foo () {
        return (T) this;
    }
}

public class Child extends Parent<Child> {
    public void bar () {
        Child c = foo();
    }
}

Ответ 2

Я написал этот образец с использованием дженериков на основе ваших потребностей.

class Parent {
    public <T extends Parent> T foo() {
        return (T)this;
    }
}

class Child extends Parent {

}

class AnotherChild extends Parent {

}

public class Test {
    public static void main(String[] args) {

        Parent p = new Child();
        System.out.println(p);
        Child c = p.foo();
        System.out.println(c);
        //throws ClassCastException here since Child is not AnotherChild
        AnotherChild ac = p.foo();
        System.out.println(ac);
    }
}

Ответ 3

Здесь улучшается метод OldCurmudgeon:

public class Parent<This extends Parent<This>> {

    @SuppressWarnings("unchecked")
    private final This THIS = (This)this;

    public This foo() {
        //...
        return THIS;
    }

    public This bar() {
        //...
        return THIS;
    }    
}

public class Child extends Parent<Child> {

    // you can override super with covariant return type
    @Override
    public Child bar() {
        super.bar();

        return this;
    }
}


Child child = 
new Child()
.foo()
.bar();

Улучшение делает неконтролируемый отбор только один раз и сохраняет его в постоянном ЭТО, который можно повторно использовать как возвращаемое значение для цепочки методов.

Это удаляет неконтролируемые приведения в каждом методе и делает код более понятным.

Вы можете переопределить родительские методы, вернув Child вместо этого (ковариантный тип возврата).

Ответ 4

Вы можете просто бросить его.

ChainChild child = new ChainChild();
((ChainChild) child.foo()).bar();

Вы можете попробовать:

public ? extends Chain foo() {
    return this;
}

Но я не уверен, что это поможет, даже если он компилируется.