Java中的面向对象编程:类继承与方法链

18
我有一个父类,它定义了一组链接方法(返回 "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. 

我很好奇,您在什么情况下使用这样的 (child.foo().bar();) 调用? - vijay
@vijay,所有子类都会使用某些设置。例如,一个方法需要一个布尔参数来启用/禁用某个设置。 - Michael
1
@vijay 如果您感到好奇,可以在此处找到该类:http://code.google.com/p/ez-vcard/source/browse/trunk/src/main/java/ezvcard/Ezvcard.java。我已经找到了某种解决方案,但是想知道是否有更好的方法来完成它 :)。 - Michael
4个回答

15
父类中返回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();
    }
}

我明白了。但问题在于,即使它是同一个对象,我也无法调用子类中的方法,因为该对象被强制转换为父类。 - Michael
3
我认为@luiggi的技巧可能更整洁。 - OldCurmudgeon

13

根据您的需求,我使用泛型编写了这个示例。

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

嗯,不错,尽管从技术上讲,它只是移动了强制类型转换的位置。 - Philip Whitehouse
1
@PhilipWhitehouse 这就是 OP 所寻找的。 - Luiggi Mendoza
问题在于 T 是从变量声明中推断出来的,而不是从类本身推断出来的。 - d4n3

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

改进是只进行一次未检查的转换并将其保存为一个常量THIS,您可以将其重用作方法链接的返回值。
这样可以消除每个方法中的未检查转换,并使代码更清晰。
您可以通过返回Child而不是This(协变返回类型)来覆盖Parent的方法。

0

你可以直接进行类型转换。

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

你可以尝试以下方法:
public ? extends Chain foo() {
    return this;
}

但我不确定这会有帮助,即使它编译通过了。


3
呃,好丑啊。我不想使用它 :) - Michael
“? extends” 可能有效,但我不确定 - 本质上,您正在尝试假设作为封装基础的隐藏信息。 - Philip Whitehouse
3
第二个案例无法编译。 - pavko_a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接