【问题标题】:Call method with implicit downcast带有隐式向下转换的调用方法
【发布时间】:2017-05-29 16:11:43
【问题描述】:

我有一个列表定义为

List<ParentClass> parentClassList;

父类是抽象的,所以当我向列表添加元素时,我会做类似的事情

parentClassList.add(new ChildClassOne(...));
parentClassList.add(new ChildClassTwo(...));

等等...我现在实际上有 5 个子类。

我想做的是调用另一个类中的方法,覆盖它的参数,所以:

public void doSomething(ChildClassOne arg) {...}
public void doSomething(ChildClassTwo arg) {...}

但是如果List的类型是父类我就不行了

doSomething(parentClassList.get(0));

基本上我需要根据孩子的类型执行不同的操作,并且我需要访问特定孩子内部的所有方法。不同的孩子有不同的方法,他们只有一个共同的方法。

【问题讨论】:

  • 代替doSomething(ChildClassOne arg),使用parentClass.doSomething(),并在每个子类中覆盖doSomething()。这就是多态的原理。
  • 基本上泛型不是协变的。您不能更改泛型参数的类型。你必须把ParentClass 拿出来,因为那是你所说的,你不能改变它。
  • “覆盖”是什么意思?

标签: java oop design-patterns


【解决方案1】:

ParentClass 中定义一个抽象方法doSomthing(),并在每个子类中这样实现:

class ChildClassOne {
    void doSomething() {
        instanceOfSomeOtherClass.doSomething(this); // will call the correct method
    }
}

并且 SomeOtherClass 将有每个子类的方法:

class SomeOtherClass {
    void doSomething(ChildClassOne o) {};
    void doSomething(ChildClassTwo o) {};
    void doSomething(ChildClassThree o) {};
    ...
}

您可以阅读有关Visitor Pattern 的这种方法

不过,如果您将代码从 SomeOtherClass 移到 ChildClasses 中,可能会更简单。

【讨论】:

  • 这基本上是答案。您应该添加它,然后在 doSomething() 方法中将其参数类型更改为 ParentClass
  • @markspace 不,这个模式的好处是SomeOtherClass 中的每个子类型都有一个特定的方法,所以你不必做instanceofs
  • 或者特殊行为可以在子类中实现doSomething()...
  • @TimothyTruckle :D 刚刚添加了这一点作为我自己的最后一条建议。
【解决方案2】:

在这种情况下,称为Visitor Pattern 的双重调度技术变得非常方便:

interface Visitor {
    void visitChildOne(ChildOne child);
    void visitChildTwo(ChildTwo child);
    void visitChildThree(ChildThree child);
}

abstract class ParentClass {
    public abstract void accept(Visitor v);
    ...
}

class ChildClassOne extends ParentClass {
    @Override
    public void accept(Visitor v) { v.visitChildOne(this); }
}
class ChildClassTwo extends ParentClass {
    @Override
    public void accept(Visitor v) { v.visitChildTwo(this); }
}
class ChildClassThree extends ParentClass {
    @Override
    public void accept(Visitor v) { v.visitChildThree(this); }
}

当您需要根据孩子的类型执行某些执行不同操作的任务时,请提供Visitor 接口的实现,并将其传递给您希望处理的每个孩子的accept

Visitor v = new Visitor() {
    @Override
    public void visitChildOne(ChildOne child) {
        System.out.println("Visiting child type 1");
        String someProperty = child.getPropertySpecificToChildOne();
    }
    @Override
    public void visitChildTwo(ChildTwo child) {
        System.out.println("Visiting child type 2");
        int someProperty = child.getPropertySpecificToChildTwo();
    }
    @Override
    public void visitChildThree(ChildThree child) {
        System.out.println("Visiting child type 3");
    }
};
for (Parent p: parentClassList) {
    p.accept(v);
}

【讨论】:

  • 我认为这在 OPs 示例中根本不起作用。你能告诉我们这是如何工作的吗?
  • @markspace 这个模式是为了做 OP 想做的事情而发明的。在 OP 的情况下,没有什么可以阻止这种模式的工作。
  • 如果需要的话,这个模式的一个很好的变体是使访问者通用(Visitor),使所有的 visit() 方法返回 T,并使 accept() 方法通用并且也返回 T。
  • @markspace 我很好奇您已经在另一个答案中立即识别出这种模式,其中doSomething 被重载,并且认为它不会在我的版本中工作,它消除了重载让人们在没有事先接触过的情况下更容易理解这种模式。
  • 你得到了我的支持 :-)。我还发现当重载和泛型不在图片中时,更容易理解这种模式。但对于其他读者,我认为这条评论可能有用。
【解决方案3】:

基本上我需要根据孩子的类型执行不同的操作,并且我需要访问特定孩子内部的所有方法。

在这种情况下,您要实现的行为属于这个专门的类。

您可以将另一个(抽象)方法签名添加到您在子类中实现的基类中,以提供类型相关的行为。

【讨论】:

  • 这正是您最终在子类中使用未实现方法的方式。您建议使用 fly 方法的 Animal 类。
  • @Sam "你建议使用带有 fly 方法的 Animal 类" - 不,我建议使用带有抽象 move 方法的类 Animal 已实现被一些 Bird 子类“玩弄”。
  • 他说他需要访问Bird中的所有方法?
  • @Sam 他说:“基本上我需要根据孩子的类型执行不同的操作”。在我看来,这种行为确实属于“孩子的类型”...... :o)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-09
  • 1970-01-01
  • 2020-01-09
  • 1970-01-01
  • 2012-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多