Search code examples
javareflectionmethodhandlefunctional-interface

Forward call on a functional interface using method handles


TL;DR: I'm trying to find the most effective to dynamically create an implementation of a functional interface that forwards calls to a set of instances of that interface.

The goal is to simplify the implementation of interfaces which allow client code to register themselves for multiple events by passing a handler (implemented as a functional interface) to the appropriate registration function. An example:

/** 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);
    }
}

This may be one example of such an interface. There would be many such interfaces, each with different events, so the implementation should be as simple as possible, without much duplicate code. I imagine that this may best be possible by introducing a type Event for internal use in such an implementation, which allows forwarding calls to all registered handlers:

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

An implementation of TrainStation could then look like this:

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

This is a manageable level of required code. The bulk of it can be extracted to an abstract class and thus is only necessary once per interface. Of course, any way to reduce it is a plus but I can't see a straightforward way without exposing the Event instances.

Going forward with this approach, the interesting thing is the implementation of the interface Event. I can certainly imagine doing it with plain old reflection, which has acceptable but not really outstanding performing. But I reckon that with Java 1.7 or 1.8 there should now be a more efficient and/or more straight-forward way to implement the return value of Event.getMasterHandler() using the new APIs, especially MethodHandle, since we know that each handler interface is a functional interface. How would I go about doing this?


Solution

  • While in principle, you can use MethodHandles to construct combining handles delegating to multiple targets, there is no way to provide it in form of an efficient implementation of the functional 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 features whose overhead you wanted to avoid.


    The best solution is to accept the need for some explicit code per interface, but with the Java 8 syntax this is so compact, that it might remove any need for a generic reflective solution:

    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");
        }
    }
    

    Note that the actual type of the Collection holding the listeners is not hardwired into the interfaces, in fact, any Iterable will do. So I use Set here as an example but you may create a more sophisticated solution specialized to listener storage, if you wish. It can be entirely Generic as your question intended, it only has to implement Iterable while the calling of each listener using the right argument types will be handled by the listener interfaces.