【问题标题】:Can you automatically cast an object of one class to a subclass and call an overloaded method in Java?能否自动将一个类的对象转换为子类并在 Java 中调用重载方法?
【发布时间】:2012-07-07 15:58:53
【问题描述】:

我有以下设置:

class Base {};
class ImplA extends Base {};
class ImplB extends Base {};
class ImplC extends Base {};

Base baseFactory(int type) {
    switch(type) {
    case 0:
        return new ImplA();
    case 1:
        return new ImplB();
    case 2:
        return new ImplC();
}

Base a = baseFactory(0);
Base b = baseFactory(1);
Base c = baseFactory(2);


List<Base> list = new ArrayList<Base>();

list.add(a);
list.add(b);
list.add(c);

// Somewhere else I have:
interface BaseHandler {
    process(ImplA a);
    process(ImplB b);
    process(ImplC c);
};

现在,我想做的事情大致如下:

class Processor {

BaseHandler bh;

Processor(BaseHandler bh) {
    this.bh = b;
}

void processList(List<Base> list) {

    for (Base x : list) {
        bh.process(x);
    }
}

然后让用户实现 BaseHandler 并能够构造一个 Processor 来对 Base 列表中的每个元素进行操作。

但是,这不起作用,因为未定义 process(Base)。只添加 3 个 if 语句似乎很简单,但我已经有了一个类似 switch 的结构来构建扩展 Base 的类的实例。似乎没有必要一遍又一遍地重复这一点。有没有办法实现这个想法,而不需要编写一个中间步骤来确定列表中每个 Base 的运行时类并调用适当的方法(实际上是另一个 switch 案例——但它会是 if 的)?

我认为一种解决方法是让每个 Base 都有一个抽象的 process 方法,该方法需要由 Impl 类实现。但是,这在我的情况下是不可接受的,因为用户不会实现 Impl 类。基本上,我需要 process 作为用户定义的回调。此外,process 成为 Impl 或 Base 类的成员是没有意义的,因为它没有任何关系。这是一个单独的回调,需要动态响应调用它的类型。并且类型总是保证是 Base 的子类。

【问题讨论】:

  • 不。你已经列出了所有的可能性。记住,重载决议总是在编译时完成。如果你想调用一个特定的重载,你必须在编译时知道这个对象有那个类。 (当然,显式测试和强制转换就足以向编译器证明对象的类型。)

标签: java polymorphism overloading factory-pattern


【解决方案1】:

您确实需要您描述的“中间步骤”,但它不必是 if 语句。您正在寻找的是使用visitor pattern 的双重调度。基本上你的Base 类会有一个方法:

void accept(BaseHandler handler);

每个子类都将其实现为:

handler.process(this);

this 将在编译时解析为每个子类中的正确类型。

【讨论】:

  • "我认为一个解决方法是让每个 Base 都有一个抽象的流程方法,需要由 Impl 类实现。但是,这在我的情况下是不可接受的,因为用户不会正在实现 Impl 类。”那怎么不排除这个答案呢?
  • @LouisWasserman:在这种情况下,实际的处理逻辑将驻留在 Impl 类中。另一方面,双重调度只是将调用路由到正确处理程序的一种方式;逻辑仍在BaseHandler 实现中。
  • @LouisWasserman 这实际上是一个完美的解决方案,因为它不需要用户实现每个 Impl 类。我排除的是用户必须实现 Impl 类。这使用户能够定义为每种类型动态调用的自定义代码,同时将所有逻辑聚集在一个地方。
【解决方案2】:

您正在寻找的是访问者模式。你在Base 上放了一个抽象方法,但它所做的只是在BaseHandler 中调用适当的方法:

public interface Base {
    void acceptHandler(BaseHandler handler);
}

然后你的具体实现会覆盖acceptHandler 并调用正确的重载。

public class ImplA implements Base {
    public void acceptHandler(BaseHandler handler) {
        handler.process(this);
    }
}

在这一点上,重载没有多大价值,最好只给你的方法提供描述性的名称。

【讨论】:

    【解决方案3】:

    听起来你想要的是 Visitor 模式:

    public interface BaseVisitor {
      void caseA(ImplA a);
      void caseB(ImplB b);
      void caseC(ImplC c);
    }
    
    public class MyVisitor implements BaseVisitor {
      void visit(List<Base> bases) {
        for (Base b : bases) {
          b.accept(this);
        }
      }
      public void caseA(ImplA a) { // ... }
      public void caseB(ImplB b) { // ... }
      public void caseC(ImplC c) { // ... }
    }
    
    public abstract class Base {
      abstract void accept(BaseVisitor visitor);
    }
    
    public class ImplA {
      public void accept(BaseVisitor visitor) {
        visitor.caseA(this);
      }
    }
    public class ImplB {
      public void accept(BaseVisitor visitor) {
        visitor.caseB(this);
      }
    }
    public class ImplC {
      public void accept(BaseVisitor visitor) {
        visitor.caseC(this);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-21
      • 1970-01-01
      • 2012-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多