3

TL;DR: 我试图找到最有效的方法来动态创建功能接口的实现,该接口将调用转发到该接口的一组实例。

目标是简化接口的实现,允许客户端代码通过将处理程序(实现为功能接口)传递给适当的注册函数来为多个事件注册自己。一个例子:

/** The interface in question we want to implement with minimal effort. */
interface TrainStation {
    void onTranArrived(TrainArrivedHandler handler);

    void onPassengerArrived(PassengerArrivedHandler handler);

    interface TrainArrivedHandler {
        void apply(String track, String lineNumber);
    }

    interface PassengerArrivedHandler {
        void apply(String track);
    }
}

这可以是这种界面的一个示例。会有很多这样的接口,每个接口都有不同的事件,所以实现应该尽可能简单,没有太多重复的代码。Event我想通过在这样的实现中引入一种供内部使用的类型,这可能最好地实现,它允许将调用转发到所有注册的处理程序:

/** Used in an implementation to implement on*() functions. */
interface Event<HANDLER> {
    /** Register a handler. */
    void on(HANDLER handler);

    /** Returned instance calls all registered handlers. */
    HANDLER getMasterHandler();

    static <HANDLER> Event<HANDLER> create() {
        // Magic. May need an instance of Class<HANDLER>, which is okay.
    }
}

的实现TrainStation可能如下所示:

final class TrainStationImpl implements TrainStation {
    private final Event<TrainArrivedHandler> trainArrivedEvent = Event.create();
    private final Event<PassengerArrivedHandler> passengerArrivedEvent = Event.create();

    @Override
    public void onTranArrived(TrainArrivedHandler handler) {
        trainArrivedEvent.on(handler);
    }

    @Override
    public void onPassengerArrived(PassengerArrivedHandler handler) {
        passengerArrivedEvent.on(handler);
    }

    /** Generates some events. */
    public void makeSomethingHappen() {
        trainArrivedEvent.getMasterHandler().apply("34", "S12");
        passengerArrivedEvent.getMasterHandler().apply("2"); // Wants to catch the S12, but is obviously too late now.
    }
}

这是所需代码的可管理级别。它的大部分可以提取到一个抽象类中,因此每个接口只需要一次。当然,任何减少它的方法都是一个加号,但如果不暴露Event实例,我看不到直接的方法。

继续使用这种方法,有趣的是接口的实现Event。我当然可以想象用普通的旧反射来做这件事,它的表现可以接受但不是很出色。但我认为,对于 Java 1.7 或 1.8,现在应该有一种更有效和/或更直接的方式来实现Event.getMasterHandler()使用新 API 的返回值,尤其是MethodHandle,因为我们知道每个处理程序接口都是一个函数式接口。我该怎么做呢?

4

1 回答 1

2

虽然原则上,您可以使用MethodHandles 来构造委托给多个目标的组合句柄,但无法以函数的有效实现形式提供它interface

LambdaMetafactory创建高效的实现,但目前仅支持直接方法句柄,因此您不能在此处使用组合句柄。MethodHandleProxies支持任意句柄,但当前使用您希望避免开销的反射功能。Proxy


最好的解决方案是接受对​​ per 的一些显式代码的需要interface,但是对于 Java 8 语法,它是如此紧凑,以至于它可能不需要通用反射解决方案:

interface TrainArrivedHandler {
    void apply(String track, String lineNumber);
    static TrainArrivedHandler forAll(Iterable<TrainArrivedHandler> all) {
        return (track, lineNumber) -> all.forEach(h -> h.apply(track, lineNumber));
    }
}
interface PassengerArrivedHandler {
    void apply(String track);
    static PassengerArrivedHandler forAll(Iterable<PassengerArrivedHandler> next) {
        return track -> next.forEach(h -> h.apply(track));
    }
}

final class TrainStationImpl implements TrainStation {

    private final Set<TrainArrivedHandler> trainArrivedHandler=new HashSet<>();
    private final Set<PassengerArrivedHandler> passengerArrivedHandler=new HashSet<>();

    public void onTranArrived(TrainArrivedHandler handler) {
        trainArrivedHandler.add(handler);
    }
    public void onPassengerArrived(PassengerArrivedHandler handler) {
        passengerArrivedHandler.add(handler);
    }

    /** Generates some events. */
    public void makeSomethingHappen() {
        TrainArrivedHandler.forAll(trainArrivedHandler).apply("34", "S12");
        // Wants to catch the S12, but is obviously too late now.
        PassengerArrivedHandler.forAll(passengerArrivedHandler).apply("2");
    }
}

请注意,Collection监听器的实际持有类型并没有硬连线到interfaces 中,事实上,任何Iterable都可以。所以我Set在这里作为一个例子,但如果你愿意,你可以创建一个专门用于监听器存储的更复杂的解决方案。正如您的问题所期望的那样,它可以完全是通用的,它只需要在Iterable使用正确参数类型的每个侦听器的调用将由 listener 处理时实现interface

于 2015-02-19T20:40:41.987 回答