使用instanceof或反射基于类的类型切换方法?

时间:2013-12-19 16:03:14

标签: java reflection instanceof visitor

我有一个数据模型,其中包含一些从单个接口派生的类,如下所示:

public interface Foo extends Visitable {}

public class BarA implements Foo {
    void accept(Visitor visitor) {
        visitor.visit(this);
    }  
}

public class BarB implements Foo {
    void accept(Visitor visitor) {
        visitor.visit(this);
    }  
}

...

还有一个Visitor有一些方法可以对数据模型的类做一些事情:

public class ModelVisitor implements Visitor {

    String visit(Foo foo) {
            // ...
    }

    String visit(BarA bar) {
        // ...
    }

    String visit(BarB bar) {
        // ...
    }

    // ...
}

说,我收到了Foo类型的集合,迭代它们的元素并调用visit()

void run(List<Foo> fooList) {
    for(Foo foo : fooList) {
        // here is the question
        foo.visit();
    }
}

我如何决定调用哪个visit()方法,因为它们都是foo的子类?我有两个可能的想法:

  1. 使用instanceof并投射到正确的类型,但我想避免这种情况,因为我会在很多ifelse if条件下结束。
  2. 我的另一种方法是使用反射。该应用程序将在Java7中运行,这就是我可以在switch-case语句中使用字符串比较的原因。
  3. 示例:

    String visit(List<Foo> fooList) {
        for(Foo foo : fooList) {
        switch(foo.getClass().getName()) {
            case "BarA":
                visit((BarA) foo);
                break;
            case "BarB":
                visit((BarB) foo);
                break;
            // ...
            }
        }
    }
    

    在我看来,第二种方法看起来比使用instanceof更清晰,但反射速度很慢。我的JVM知识不是那么好,但我假设instanceof也会使用反射来获取对象的类型,因此两种解决方案之间没有真正的性能差异。

    您会使用什么或有其他方法来解决这个问题?

2 个答案:

答案 0 :(得分:0)

在访客模式中,您可以调用

foo.accept(visitor);

具体的Foo对象然后决定调用哪个Visitor方法。您不需要instanceof

例如,我删除了一些接口以使代码更小。

public class Main {

    public static void main(String[] args) {
        Bar bar1 = new BarA();
        Bar bar2 = new BarB();

        Visitor visitor = new Visitor();
        bar1.accept(visitor);
        bar2.accept(visitor);
    }

    public static class Bar {
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }

    public static class BarB extends Bar {
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }

    public static class BarA extends Bar {
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }

    public static class Visitor {

        public void visit(Bar bar) {
            System.out.println("visited: Bar");
        }

        public void visit(BarA bar) {
            System.out.println("visited: BarA");
        }

        public void visit(BarB bar) {
            System.out.println("visited: BarB");
        }

    }

}

运行示例将输出

visited: BarA
visited: BarB

答案 1 :(得分:0)

只要Foo数组中的每个对象都使用其明确的构造函数创建,就不需要其中任何一个。我认为代码解释得更好:

使用您的代码的客户端应该如下创建Foo列表:

ArrayList<Foo> list = new ArrayList<Foo>();
list.add(new BarA());
list.add(new BarB());
...

这就是你所需要的一切。调用方法visit()并传递“list”中的一个对象时,它将通过正确的方法。这是polimorphism的魔力。

如果您想确定,请继续打印您的清单:

for(Foo f : list)
System.out.println(f);

该类的名称将被打印出来,您将看到每个对象的引用都转到它的类而不是Foo: package.BarA@wholebunchfnumbers

尽管如此,我认为你仍然没有访客模式的概念。