具有非SAM接口的lambda的Java习惯用法

Jan*_*sen 11 java lambda java-8

在Java中,使用lambda而不是匿名类可以优雅地实现具有单个抽象方法(即SAM类型或功能接口)的接口:

    // SAM ActionListener with anonymous implementation
    button.addActionListener(
        new ActionListener(){
            public void actionPerformed(Event e){
                System.out.println("button via anon!");
            }
        }
    );
Run Code Online (Sandbox Code Playgroud)

可以替换为:

    // SAM ActionListener with lambda implementation
    button.addActionListener(
        e -> System.out.println("button via lambda!")
    );
Run Code Online (Sandbox Code Playgroud)

但对于具有多个抽象方法的接口,无法直接应用lambda.例如,java.awt.event.WindowListener有七种方法.但通常一块代码只对定义这七种方法中的一种感兴趣.

要使用匿名类覆盖来实现该行为,我们可以:

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

但是有一个更优雅的方式与lambdas?

@FunctionalInterface
public interface ActionListener {
    void actionPerformed(Event e);
}

public interface WindowListener {
    void windowOpened(Event e);

    void windowClosing(Event e);
}

public class WindowAdapter implements WindowListener {

    public void windowOpened(Event e){
        System.out.println("windowOpened in adapter!");
    }

    public void windowClosing(Event e){
        System.out.println("windowClosing in adapter!");
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:@ maythesource.com提出了一个类似但更广泛的问题:" 如果想要在匿名类中实现多个方法,那么有人会使用MouseListener做什么? "最受欢迎和接受的答案是使用匿名实现.我的问题是关于非SAM类型的优雅lambda解决方案.因此,这个问题是不是重复的Java 8 Lambda表达式-怎么样在嵌套类中的多个方法.


Stu*_*rks 9

Brian Goetz对另一个问题的回答中,他建议使用静态工厂方法.在这种情况下,它有点单调乏味,因为WindowListener定义了七种处理程序方法,因此您需要定义七种静态工厂方法.但这并不是那么糟糕,因为已经有一个WindowAdapter类提供了所有方法的空实现.(如果没有,你必须定义自己的等价物.)这是我如何做到的:

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

    // ...
}
Run Code Online (Sandbox Code Playgroud)

(其他253个案例是类似的.)

每个工厂方法都创建一个子类,WindowAdapter它会覆盖相应的方法来调用传入的lambda表达式.不需要额外的适配器或桥接类.

它将使用如下:

window.addWindowListener(WLFactory.windowOpened(we -> System.out.println("opened")));
Run Code Online (Sandbox Code Playgroud)


Jan*_*sen 4

我发现的最优雅的方法是使用匿名桥:

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

与 SAM 类型场景一样,它比匿名适配器更干净:

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

但它确实需要一个带有静态工厂的有点尴尬的桥:

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);
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上我发现这个方法非常好并且可读。桥确实有点尴尬,但在调用方可以立即看到发生了什么。 (4认同)
  • 这个解决方案的奇怪之处在于创建了这些过时的类型“WindowOpened”和“WindowClosing”,而该解决方案没有它们也可以工作,即直接创建“WindowAdapter”的匿名子类。还不清楚为什么“WindowBridge”是一个“接口”,因为它不适合由任何人实现。 (4认同)
  • 只是稍微干净一点。 (2认同)