如何将类的子类型的实例传递给相应的重载方法?

时间:2010-07-01 16:53:24

标签: java overloading

我正在重构一个大量使用case语句和emums的现有状态机。我把它分解为Events和eventhandler对象,每个对象对应一个状态机。 eventHandlers返回新事件以通过状态机传播。

所以,我的代码看起来像这样:

public class Event {
    //common fields and methods
}

public class SpecificEvent extends Event {
    //fields specific to this event
}

public class AnotherEvent extends Event {
    //fields specific to this event
}

public class EventHandler {
    public Event handleEvent(SpecificEvent evt) {
        //do default something
    }
    public Event handleEvent(AnotherEvent evt) {
        //do default something else
    }
}

public class StateOneEventHandler extends EventHandler {
    public Event handleEvent(SpecificEvent evt) {
        //do State1 things
    }
    public Event handleEvent(AnotherEvent evt) {
        //do other State1 things
    }
}

public class StateTwoEventHandler extends EventHandler {
    public Event handleEvent(SpecificEvent evt) {
        //do State2 things
    }
    public Event handleEvent(AnotherEvent evt) {
        //do other State2 things
    }
}

我的问题和疑问是:我只在我的状态机中传递通用事件引用,那么如何为事件调用正确的处理程序呢?

Event evt = new SpecificEvent();
EventHandler handler = new StateOneEventHandler();

//... later

handler.handleEvent(evt); //compiler error

完成此运行时事件“dispatch”的最佳方法是什么?

3 个答案:

答案 0 :(得分:4)

你是正确的,重载的方法将无法解决这个问题(因为要在调用时确定要调用的方法)。

我建议你需要重构这个,要么将一个方法添加到event中,该方法将处理程序作为参数(类似public void act(EventHandler handler)),或者重写你的处理程序,它不需要需要强烈了解事件的类型。如果Event是一个合理的接口/超类,它将自己暴露足够的功能,以便EventHandler不需要来了解特定的类型。

如果你真的需要,你当然可以总是施放,但一般来说你应该遵循Demeter法则并且只能通过事件界面与事件对象进行交互。

答案 1 :(得分:1)

决定调用哪条消息是在编译时完成的。因此,为了选择特定的方法变体,您需要添加一个强制转换。

您可能希望查看看似适合您的用例的访问者模式:http://en.wikipedia.org/wiki/Visitor_pattern,以获得组织类层次结构的方法,以便不需要强制转换。

另一种选择可能是将逻辑放入事件中,以便您可以使用正常的后继绑定机制。

答案 2 :(得分:0)

如何在EventHandler中使用另一种方法:

public Event handleEvent(Event evt) {
    if (evt instanceof SpecificEvent) {
        return handleEvent((SpecificEvent)evt);
    }
    if (evt instanceof AnotherEvent) {
        return handleEvent((AnotherEvent)evt);
    }
    // code for unknown type
}

如果你有一个Event类型的变量,那么将调用它并尝试调用其他定义的方法之一。如果它是一个没有定义方法的类型扩展事件,它包含该案例的代码。在扩展EventHandler的类中,将调用覆盖的方法。

我知道它不是很漂亮,但它应该有用。

编辑:

你也可以尝试反思:

public Event handleEvent(Event evt) throws InvocationTargetException{
    try {
        Method m = this.getClass().getMethod("handleEvent", evt.getClass());
        return (Event) m.invoke(this, evt);
    } catch (NoSuchMethodException nsme) {
        nsme.printStackTrace();
    } catch (IllegalAccessException iae) {
        iae.printStackTrace();
    } catch (InvocationTargetException ite) {
        ite.getCause().printStackTrace();
        throw ite;
    }
    // code for unknown type
}

如果你有很多事件类型,这会更好。唯一的问题是,现在你必须处理例外情况。我的猜测是,如果发生NoSuchMethodExceptionIllegalAccessException,您可以忽略它们,因为这意味着没有为该事件定义任何方法(或者它无法访问),因此您需要恢复默认处理未知类型。您可能不想忽略InvocationTargetException,因为这意味着该方法被调用但方法本身引发了异常,因此这意味着该方法中的代码存在问题。