使用方法句柄转发对功能接口的调用

Forward call on a functional interface using method handles

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.
    }
}

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

继续这种方法,有趣的是接口的实现 Event。我当然可以想象用普通的旧反射来做这件事,它的表现可以接受但并不是很出色。但我认为 Java 1.7 或 1.8 现在应​​该有更高效 and/or 更直接的方法来使用新 API 实现 Event.getMasterHandler() 的 return 值,特别是 MethodHandle,因为我们知道每个处理程序接口都是一个功能接口。我该怎么做?

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

LambdaMetafactory creates efficient implementation but currently supports direct method handles only, thus you can’t use a combining handle here. MethodHandleProxies supports arbitrary handles but currently utilizes the Reflection Proxy 个您希望避免开销的功能。


最好的解决方案是接受每个 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 持有侦听器的实际类型并未硬连接到 interface 中,事实上,任何 Iterable 都可以。因此,我在这里使用 Set 作为示例,但如果您愿意,您可以创建一个专门用于侦听器存储的更复杂的解决方案。它可以像您的问题一样完全是通用的,它只需要实现 Iterable 而使用正确参数类型的每个侦听器的调用将由侦听器 interfaces.

处理