避免使用 instanceof

4
我有一组具有共同超类的POJOs。它们存储在一个类型为超类的二维数组中。现在,我想从数组中获取一个对象并使用子类的方法。这意味着我必须将它们转换为子类。是否有一种方法可以在不使用instanceof的情况下实现这一点?
更新:具体示例请参见:http://obviam.net/index.php/the-mvc-pattern-tutorial-building-games/ 查看:"单击敌人时添加新动作(攻击)"

3
为什么你不想使用 instanceof? - TS-
3
或者为什么不定义一个抽象方法(或接口),让每个子类都可以使用它来执行你想要做的工作呢? - atk
3
首先丢弃您以后需要的类型信息是不专业和不美观的。如果您需要知道对象是子类类型,请不要将其放入超类数组中。;) - Louis Wasserman
2
@tsOverflow:实际上,instanceof 被认为是 hacky 的,因为如果将来添加了更多的子类,你必须在代码中寻找所有使用情况,以确定在哪里添加新代码。相比之下,访问者模式或重写方法将行为限制在一个地方。因此,从代码维护的角度来看,instanceof 是不可取的。 - Kevin
1
@tsOverflow:你好世界是一个草人论点。根据我的经验,“丢弃原型”会被构建到“生产质量”并永远不会被丢弃。因此,在专业环境中,我认为没有任何借口可以解释次优设计。请注意,我并不提倡访问者模式——我几乎从不使用它。但是,使用instanceof而不是覆盖方法是不可原谅的。 - Kevin
显示剩余5条评论
5个回答

6

是的 - 你可以通过反转流程来实现:不是在基类实例属于特定类型时让你的代码执行某些操作,而是将一个动作项传递给对象,让对象决定是否执行它。这就是访问者模式背后的基本技巧。

interface DoSomething {
    void act();
}
abstract class AbstractBaseClass {
    abstract void performAction(DoSomething ds);
}
class FirstSubclass extends AbstractBaseClass {
    public void performAction(DoSomething ds) {
        ds.act();
    }
}
class SecondSubclass extends AbstractBaseClass {
    public void performAction(DoSomething ds) {
        // Do nothing
    }
}

AbstractBaseClass array[] = new AbstractBaseClass[] {
    new FirstSubclass()
,   new FirstSubclass()
,   new SecondSubclass()
,   new FirstSubclass()
,   new SecondSubclass()
};
for (AbstractBaseClass b : array) {
    b.performAction(new DoSomething() {
        public void act() {
            System.out.println("Hello, I'm here!");
        }
    });
}

这是我首选的方法。不幸的是,我无法向子类添加方法,因为它们是没有任何功能的POJO。 - user28061
1
如果您的子类没有覆盖,那么您的选择相当有限:所有操作都将限于作用于您的子类的代码中,因此最终会得到一个 instanceof - Sergey Kalinichenko
2
整个 POJO 中不能包含功能的概念从何而来?我在其他一些项目中看到过这种说法。对象旨在包含数据和行为。如果它们只包含数据,那么它们就不是 POJOs...它们只是 beans。 - corsiKa

4
如果您知道它们是子类类型,那么可以直接进行转换而不需要使用instanceof检查。但是将它们放入超类类型的数组中,就是告诉编译器丢弃它们实际上是子类类型的信息。您的超类应该公开这些方法(可能作为抽象方法),或者数组应该是子类类型(这样您就不需要告诉编译器忘记对象的实际类型),否则您只能承受并进行转换(可能需要使用instanceof检测)。唯一值得注意的替代方案是您可以尝试访问者模式,该模式将一个动作传递给对象,并允许对象根据其运行时类型决定要执行什么操作。这使您可以覆盖类以忽略或执行基于其运行时类型的操作。

2
你可以尝试使用访问者设计模式。 http://en.wikipedia.org/wiki/Visitor_pattern 你需要问自己,为什么需要知道它们的类型,也许可以用超类中的抽象方法替代,每个子类都可以根据所需结果实现该方法。
abstract class A{
    abstract void visit();
}

class B extends A{
    void visit() { print("B"); }
}

class C extends A {
    void visit() { print("C"); }
}

1

我会尽量避免首先将它们投射出来。

真正思考一下你想要做什么,以及它们是否应该像那样在同一个集合中。

如果你有这样的东西

for(MyObj o : array) {
   if(o instanceof A) {
      ((A)o).doA();
   }
   if(o instanceof B) {
      ((B)o).doB();
   }
}

考虑改为这个

abstract class MyObj {
    abstract void doIt();
}

class A {
    void doIt() { doA(); }
}

class B {
    void doIt() { doB(); }
}

0

将方法暴露在超类中,然后使用覆盖。在基类中提供一个空实现,以便子类可以在需要时忽略该操作。


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