Lambda 在Java8中分配多个JCheckBox侦听器的冗余程度较低的方法?(例如,方法参考?)

Lambda 在Java8中分配多个JCheckBox侦听器的冗余程度较低的方法?(例如,方法参考?),lambda,java-8,method-reference,Lambda,Java 8,Method Reference,模式始终相同: 将新的ActionListener添加到JCheckBoxX。侦听器的主体执行以下操作: 获取对象Y参考。如果参考=null,请执行以下操作: 使用参数X调用Y上的方法Z。isSelected() 示例代码: jChecKBoxWindowSizeLocked.addActionListener(e -> { final WindowConfig lastWin = getLastTouchedWindowConfig(); if (lastWin !=

模式始终相同:

  • 将新的ActionListener添加到JCheckBoxX。侦听器的主体执行以下操作:
  • 获取对象Y参考。如果参考=null,请执行以下操作:
  • 使用参数X调用Y上的方法Z。isSelected()
示例代码:

jChecKBoxWindowSizeLocked.addActionListener(e -> {
    final WindowConfig lastWin = getLastTouchedWindowConfig();
    if (lastWin != null) {
        lastWin.setSizeLocked(jChecKBoxWindowSizeLocked.isSelected());
    }
});
jChecKBoxRememberSize.addActionListener(e -> {
    final WindowConfig lastWin = getLastTouchedWindowConfig();
    if (lastWin != null) {
        lastWin.setRememberSize(jChecKBoxRememberSize.isSelected());
    }
});
etc.
这看起来是非常多余的,似乎应该用Lambda或方法来解决,但我如何在不确定对象Y的情况下引用方法Z呢?我尝试了此方法,但我使用的调用可能不正确:

private void addCheckboxListener(final JCheckBox checkBox, final Consumer<Boolean> setter) {
    checkBox.addActionListener(e -> {
        if (setter != null) {
            setter.accept(checkBox.isSelected());
        }
    });
}
我非常确定,这提供了对getter在调用add方法时返回的任何内容的引用,而不是稍后解析的一般引用。因此,这会在其自己的行中导致NullPointerException(但编译很好):

当然,我可以只给出通过反射声明的目标类的setter方法,但这对于大约10个jcheckbox来说是完全多余的。现在,如果它只是大约10,那么我应该复制代码,而不是扮演科学家,对吗

但我的观点/问题是应该有某种Lambda-reference-y方法来实现这一点。或者这种可能性确实不存在





编辑





多亏了Holger的回答,我现在成功地尝试了使用具有附加实例参数的静态setter,这样addCheckboxListener就可以获取一个静态方法引用,从而按照预期工作。(EDIT2:不,不是静态的。请参阅对霍尔格(已接受)回复的评论。本“编辑”部分的其余部分都是在本EDIT2之前编写的。)

我不喜欢让setters保持静态,但现在看来这是最优雅的解决方案,而且它肯定是工具库中的一把好刀

由此产生了一个新问题。在我发表新文章之前,我会在这里问:

此调用中出现无法解释的编译错误:

addCheckboxListener(jCheckBoxRememberLocation, WindowConfig::setRememberLocation);
方法的头部:

private void addCheckboxListener(JCheckBox checkBox, BiConsumer<WindowConfig, Boolean> setter) {
只有在WinConfig类中既有旧的非静态setter,也有新的静态setter,并且它们都需要一组不同的参数时,才会发生错误这可能是Java错误吗?以下是方法:

public void setRememberLocation(final boolean rememberLocation) {
    this.rememberLocation = rememberLocation;
}

public static void setRememberLocation(final WindowConfig instance, final Boolean rememberLocation) {
    instance.rememberLocation = rememberLocation;
}
这一错误似乎是错误的,因为这两种方法中只有一种符合概要文件

真正奇怪的是:如果我将非静态方法的头更改为与静态方法的头完全相同,错误就会消失

但是真正的
非常奇怪的是:如果我交换非静态方法的第一个和第二个参数,错误仍然不会发生。WTF

C:\>java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

C:\Program Files\Java\jdk1.8.0_74\bin>javac -version
javac 1.8.0_74

winver
Windows 7 Ultimate
Version 6.1 (Build 7601: Service Pack 1)

该Java版本由项目使用。它也是我系统中唯一的一个。(不,其他地方也没有32位版本。)

显然,您需要这样一种方法:

private void addCheckboxListener(
    JCheckBox checkBox, BiConsumer<WindowConfig, Boolean> setter) {

    Objects.requireNonNull(setter);
    checkBox.addActionListener(e -> {
        final WindowConfig lastWin = getLastTouchedWindowConfig();
        if(lastWin != null) {
            setter.accept(lastWin, checkBox.isSelected());
        }
    });
}
应该在其上调用方法的
WindowConfig
实例将成为函数签名的一部分,并将在事件发生时获取并传递给函数


请注意,我将行为更改为立即拒绝
null
setter参数,而不是默默地忽略这种错误条件

+1谢谢,这是一种我没有想到的可能的方法。我试过了,效果很好。别担心,我最终会将此标记为已接受的答案,但我想等待其他可能的答案。现在,你能读一下我对这篇文章的长篇编辑吗?因为出现了一个新的非常奇怪的相关问题。这正是编译器告诉您的,带有参数
(WindowConfig,Boolean)
静态
方法与接受单个
布尔值的
WindowConfig
类型的实例方法具有相同的功能签名,因为它们都使用
WindowConfig
实例和
Boolean
。要解决这个问题,只需删除
静态
方法,我完全不明白为什么要添加它们…另请参阅,我只是假设如果使用两个参数调用setter.accept(),则该方法需要有两个参数,因此我在逻辑上添加了静态版本。我不知道后台的Lambda/method参考实现是如此灵活。我想我现在有点明白是什么导致了这个错误,以及为什么它的行为是这样描述的。既然你的答案似乎是这个问题的黄金解决方案,我就把它标为被接受。谢谢,伙计!好的,您不能创建像
BiConsumer[]
这样的数组,因为泛型和数组不能很好地协同工作,但是您可以创建
ArrayList
,如果有足够的初始大小,它的性能不会比数组差。或者使用类似于
List List=Arrays.asList(WindowConfig::SetMemberSize,WindowConfig::SetMemberLocation)
,它甚至具有类似于数组初始值设定项的形式。@第二个问题:WindowConfig::SetMemberLocation可以解释为接受WindowConfig和布尔值的静态方法,或WindowConfig中接受布尔值的实例方法。我建议保留instance方法。@srborlongan谢谢,Holger刚刚告诉我同样的情况,所以我相应地更改了所有内容(效果很好),现在将对文章进行一点编辑。
public void setRememberLocation(final boolean rememberLocation) {
    this.rememberLocation = rememberLocation;
}

public static void setRememberLocation(final WindowConfig instance, final Boolean rememberLocation) {
    instance.rememberLocation = rememberLocation;
}
C:\>java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

C:\Program Files\Java\jdk1.8.0_74\bin>javac -version
javac 1.8.0_74

winver
Windows 7 Ultimate
Version 6.1 (Build 7601: Service Pack 1)
private void addCheckboxListener(
    JCheckBox checkBox, BiConsumer<WindowConfig, Boolean> setter) {

    Objects.requireNonNull(setter);
    checkBox.addActionListener(e -> {
        final WindowConfig lastWin = getLastTouchedWindowConfig();
        if(lastWin != null) {
            setter.accept(lastWin, checkBox.isSelected());
        }
    });
}
addCheckboxListener(cbRememberSize,     WindowConfig::setRememberSize);
addCheckboxListener(cbRememberLocation, WindowConfig::setRememberLocation);