在Java中,使用lambda而不是匿名类可以优雅地实现具有单个抽象方法的接口(即SAM类型或功能接口):
// SAM ActionListener with anonymous implementation
button.addActionListener(
new ActionListener(){
public void actionPerformed(Event e){
System.out.println("button via anon!");
}
}
);
可以替换为:
// SAM ActionListener with lambda implementation
button.addActionListener(
e -> System.out.println("button via lambda!")
);
但对于具有多个抽象方法的接口,无法直接应用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!");
}
}
);
但是有一个更优雅的方式与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!");
}
}
注意:@ maythesource.com提出了一个类似但更广泛的问题:" 如果想要在匿名类中实现多个方法,有人会对MouseListener做些什么? "最受欢迎和接受的答案是使用匿名实现。我的问题是关于非SAM类型的优雅lambda解决方案。因此,此问题不是Java 8 Lambda Expressions - what about multiple methods in nested class的重复。
答案 0 :(得分:7)
在Brian Goetz' answer to the other question中,他建议使用静态工厂方法。在这种情况下,由于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); }
};
}
// ...
}
(The other 253 cases are analogous.)
每个工厂方法都创建一个WindowAdapter
的子类,它覆盖适当的方法来调用传入的lambda表达式。不需要额外的适配器或桥接类。
将使用如下:
window.addWindowListener(WLFactory.windowOpened(we -> System.out.println("opened")));
答案 1 :(得分:4)
我找到的最优雅的方法是使用匿名桥:
// SAM bridge with lambda implementation
window.addWindowListener(
WindowBridge.windowOpened(
b -> System.out.println("opening via lambda!")
)
);
,与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!");
}
}
);
但它需要一个带有静态工厂的稍微笨拙的桥:
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);
}
};
}
}
答案 2 :(得分:1)
我想为此提出一个相当通用的解决方案:可以使用Dynamic Proxy Classes来生成接口的实现。这样的代理可以简单地忽略所有方法,除了将适当的Consumer
指定为lambda的方法。
当然,必须小心使用反射。但它的优点是它可以开箱即用&#34;开箱即用&#34;使用任何 MAM接口类型(多个抽象方法)。
不需要为所有接口及其方法创建数十个或数百个桥接方法。只需创建一个&#34;空的&#34;接口的实现,并将单个方法实现作为lambda传递。
这里有一个基本的示例实现,表明它可以简洁而通用地用于不同的接口,例如WindowListener
,MouseListener
和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());
}
}
答案 3 :(得分:0)
在Java 8中,您可以提供接口方法的默认实现。创建一个扩展WindowListener的新接口,该接口除了已实现的所有方法外:
@FunctionalInterface
public interface WindowOpenedListener extends WindowListener {
@Override
default void windowClosing(WindowEvent e) {}
@Override
default void windowClosed(WindowEvent e) {}
@Override
default void windowIconified(WindowEvent e) {}
@Override
default void windowDeiconified(WindowEvent e) {}
@Override
default void windowActivated(WindowEvent e) {}
@Override
default void windowDeactivated(WindowEvent e) {}
}
如您所见,windowOpened是界面上唯一未实现的方法,因此您可以:
window.addWindowListener((WindowOpenedListener) e -> {
// windowsOpened implementation
});
可以推广多种方法(如果有意义),以实现WindowListener的所有方法并添加新的抽象方法,例如:
@FunctionalInterface
public interface WindowStatusListener extends WindowListener {
void statusChanged(WindowEvent e);
@Override
default void windowOpened(WindowEvent e) { statusChanged(e); }
@Override
default void windowClosing(WindowEvent e) {}
@Override
default void windowClosed(WindowEvent e) { statusChanged(e); }
@Override
default void windowIconified(WindowEvent e) {}
@Override
default void windowDeiconified(WindowEvent e) {}
@Override
default void windowActivated(WindowEvent e) {}
@Override
default void windowDeactivated(WindowEvent e) {}
}
然后
window.addWindowListener((WindowStatusListener) e -> {
// statusChanged implementation
});