Java idiom for lambdas with non-SAM interfaces

The most elegant way I have found is to use an anonymous bridge:

    // SAM bridge with lambda implementation
    window.addWindowListener(
        WindowBridge.windowOpened(
            b -> System.out.println("opening via lambda!")
        )
    );

which, like the SAM type scenario, is cleaner than the anonymous adapter:

    // non-SAM with adapter implementation with override
    window.addWindowListener(
        new WindowAdapter() {
            @Override
            public void windowOpened(Event e){
                System.out.println("WindowAdapter opened via override!");
            }
        }
    );

but it does require a slightly awkward bridge with a static factory:

import java.util.function.Consumer;

public interface WindowBridge {

    // SAM for this method
    public abstract class WindowOpened extends WindowAdapter {
        public abstract void windowOpened(Event e);
    }

    // factory bridge
    public static WindowOpened windowOpened(Consumer<Event> c) {
        return new WindowOpened() {
            public void windowOpened(Event e){
                c.accept(e);
            }
        };
    }

    // SAM for this method
    public abstract class WindowClosing extends WindowAdapter {
        public abstract void windowClosing(Event e);
    }

    // factory bridge
    public static WindowClosing windowClosing(Consumer<Event> c) {
        return new WindowClosing() {
            public void windowClosing(Event e){
                c.accept(e);
            }
        };
    }
}

In Brian Goetz' answer to the other question, he suggested using static factory methods. In this case it's a bit tedious, since WindowListener defines seven handler methods, so you'd need to define seven static factory methods. This isn't that bad, though, since there is already a WindowAdapter class that provides empty implementations of all of the methods. (If there isn't one, you'd have to define your own equivalent.) Here's how I'd do it:

class WLFactory {
    public static WindowListener windowOpened(Consumer<WindowEvent> c) {
        return new WindowAdapter() {
            @Override public void windowOpened(WindowEvent e) { c.accept(e); }
        };
    }

    public static WindowListener windowClosing(Consumer<WindowEvent> c) {
        return new WindowAdapter() {
            @Override public void windowClosing(WindowEvent e) { c.accept(e); }
        };
    }

    // ...
}

(The other 253 cases are analogous.)

Each factory method creates a subclass of WindowAdapter that overrides the appropriate method to call the lambda expression that's passed in. No need for additional adapter or bridge classes.

It would be used as follows:

window.addWindowListener(WLFactory.windowOpened(we -> System.out.println("opened")));

I'd like to propose a rather generic solution for this: One can use Dynamic Proxy Classes to generate the implementation of the interface. Such a proxy could simply ignore all methods, except for the method for which an appropriate Consumer was specified as a lambda.

Of course, reflection always has to be used with care. But the advantage is that it works "out of the box" with any MAM-interface-type (Multiple Abstract Method).

There's no need to create dozens or hundreds of bridge methods for all the interfaces and their methods. Just create a proxy that is an "empty" implementation of the interface, and pass in a single method implementation as a lambda.

A basic example implementation is here, showing that it may be used concisely and generically for different interfaces, like WindowListener, MouseListener and ComponentListener:

import java.awt.event.ComponentListener;
import java.awt.event.MouseListener;
import java.awt.event.WindowListener;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.function.Consumer;
import java.util.function.Function;

class LambdaDelegatorTest
{
    public static void main(String args[])
    {
        WindowListener w =
            LambdaDelegators.create(WindowListener.class, "windowClosed",
                e -> System.out.println("Window closed"));

        w.windowActivated(null);
        w.windowClosed(null);

        MouseListener m =
            LambdaDelegators.create(MouseListener.class, "mouseExited",
                e -> System.out.println("Mouse exited"));

        m.mouseClicked(null);
        m.mouseExited(null);

        ComponentListener c =
            LambdaDelegators.create(ComponentListener.class, "componentShown",
                e -> System.out.println("Component shown"));

        c.componentHidden(null);
        c.componentShown(null);

    }
}

class LambdaDelegators
{
    public static <T> T create(Class<T> c, String methodName,
        Consumer<Object[]> consumer)
    {
        Function<Object[], Object> function = new Function<Object[], Object>()
        {
            @Override
            public Object apply(Object[] t)
            {
                consumer.accept(t);
                return null;
            }
        };
        return createFromFunction(c, methodName, function);
    }

    @SuppressWarnings("unchecked")
    private static <T> T createFromFunction(Class<T> c, String methodName,
        Function<Object[], Object> function)
    {
        Class<?> classes[] = new Class[1];
        classes[0] = c;
        Object proxy =
            Proxy.newProxyInstance(c.getClassLoader(), classes,
                new LambdaDelegator(methodName, function));
        return (T) proxy;
    }

    private LambdaDelegators()
    {

    }
}

class LambdaDelegator implements InvocationHandler
{
    private static final Method hashCodeMethod;
    private static final Method equalsMethod;
    private static final Method toStringMethod;
    static
    {
        try
        {
            hashCodeMethod = Object.class.getMethod(
                "hashCode", (Class<?>[]) null);
            equalsMethod = Object.class.getMethod(
                "equals", new Class[] { Object.class });
            toStringMethod = Object.class.getMethod(
                "toString", (Class<?>[]) null);
        }
        catch (NoSuchMethodException e)
        {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    private final String methodName;
    private final Function<Object[], Object> function;

    public LambdaDelegator(String methodName,
        Function<Object[], Object> function)
    {
        this.methodName = methodName;
        this.function = function;
    }

    public Object invoke(Object proxy, Method m, Object[] args)
        throws Throwable
    {
        Class<?> declaringClass = m.getDeclaringClass();
        if (declaringClass == Object.class)
        {
            if (m.equals(hashCodeMethod))
            {
                return proxyHashCode(proxy);
            }
            else if (m.equals(equalsMethod))
            {
                return proxyEquals(proxy, args[0]);
            }
            else if (m.equals(toStringMethod))
            {
                return proxyToString(proxy);
            }
            else
            {
                throw new InternalError(
                    "unexpected Object method dispatched: " + m);
            }
        }
        else
        {
            if (m.getName().equals(methodName))
            {
                return function.apply(args);
            }
        }
        return null;
    }

    private Integer proxyHashCode(Object proxy)
    {
        return new Integer(System.identityHashCode(proxy));
    }

    private Boolean proxyEquals(Object proxy, Object other)
    {
        return (proxy == other ? Boolean.TRUE : Boolean.FALSE);
    }

    private String proxyToString(Object proxy)
    {
        return proxy.getClass().getName() + '@' +
            Integer.toHexString(proxy.hashCode());
    }
}