java: 是否可以将按钮数组的 lambda 表达式设置为 for 循环?如果是这样怎么办?
java: Is it possible to set a lambda expression for an array of Buttons is a for loop? If so how?
我希望能够做这样的事情:
for(i = 0; i < 10; i++) {
//if any button in the array is pressed, disable it.
button[i].setOnAction( ae -> { button[i].setDisable(true) } );
}
但是,我收到一条错误消息 "local variables referenced from a lambda expression must be final or effectively final"
。我怎么可能仍然像上面的代码那样做(如果可能的话)?如果做不到,应该怎么做才能得到类似的结果?
如错误消息所述,从 lambda 表达式引用的局部变量必须是最终的或实际上是最终的("effectively final" 意味着编译器可以为您使其成为最终的)。
简单的解决方法:
for(i = 0; i < 10; i++) {
final int ii = i;
button[i].setOnAction( ae -> { button[ii].setDisable(true) } );
}
由于您使用的是 lambda,因此您还可以受益于 Java 8 的其他功能,例如流。
例如,IntStream
:
A sequence of primitive int-valued elements supporting sequential and parallel aggregate operations. This is the int primitive specialization of Stream.
可以用来代替for循环:
IntStream.range(0,10).forEach(i->{...});
现在您有了一个可用于您目的的索引:
IntStream.range(0,10)
.forEach(i->button[i].setOnAction(ea->button[i].setDisable(true)));
您还可以从数组生成流:
Stream.of(button).forEach(btn->{...});
在这种情况下,您将没有索引,因此正如@shmosel 所建议的,您可以使用事件源:
Stream.of(button)
.forEach(btn->btn.setOnAction(ea->((Button)ea.getSource()).setDisable(true)));
编辑
正如@James_D所建议的,这里不需要向下转换:
Stream.of(button)
.forEach(btn->btn.setOnAction(ea->btn.setDisable(true)));
在这两种情况下,您还可以从并行操作中获益:
IntStream.range(0,10).parallel()
.forEach(i->button[i].setOnAction(ea->button[i].setDisable(true)));
Stream.of(button).parallel()
.forEach(btn->btn.setOnAction(ea->btn.setDisable(true)));
使用 Event
获取来源 Node
。
for(int i = 0; i < button.length; i++)
{
button[i].setOnAction(event ->{
((Button)event.getSource()).setDisable(true);
});
}
Lambda 表达式实际上类似于在流上运行的匿名方法。为了避免任何不安全的操作,Java 规定在 lambda 表达式中不能访问任何可以修改的外部变量。
为了解决这个问题,
final int index=button[i];
并在 lambda 表达式中使用索引而不是 i。
你说如果按下按钮,但在你的示例中,列表中的所有按钮都将被禁用。尝试为每个按钮关联一个侦听器,而不是仅仅禁用它。
对于逻辑,你的意思是这样的:
Arrays.asList(buttons).forEach(
button -> button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
}
}));
我也喜欢 Sedrick 的回答,但你必须在循环中添加一个动作侦听器。
我希望能够做这样的事情:
for(i = 0; i < 10; i++) {
//if any button in the array is pressed, disable it.
button[i].setOnAction( ae -> { button[i].setDisable(true) } );
}
但是,我收到一条错误消息 "local variables referenced from a lambda expression must be final or effectively final"
。我怎么可能仍然像上面的代码那样做(如果可能的话)?如果做不到,应该怎么做才能得到类似的结果?
如错误消息所述,从 lambda 表达式引用的局部变量必须是最终的或实际上是最终的("effectively final" 意味着编译器可以为您使其成为最终的)。
简单的解决方法:
for(i = 0; i < 10; i++) {
final int ii = i;
button[i].setOnAction( ae -> { button[ii].setDisable(true) } );
}
由于您使用的是 lambda,因此您还可以受益于 Java 8 的其他功能,例如流。
例如,IntStream
:
A sequence of primitive int-valued elements supporting sequential and parallel aggregate operations. This is the int primitive specialization of Stream.
可以用来代替for循环:
IntStream.range(0,10).forEach(i->{...});
现在您有了一个可用于您目的的索引:
IntStream.range(0,10)
.forEach(i->button[i].setOnAction(ea->button[i].setDisable(true)));
您还可以从数组生成流:
Stream.of(button).forEach(btn->{...});
在这种情况下,您将没有索引,因此正如@shmosel 所建议的,您可以使用事件源:
Stream.of(button)
.forEach(btn->btn.setOnAction(ea->((Button)ea.getSource()).setDisable(true)));
编辑
正如@James_D所建议的,这里不需要向下转换:
Stream.of(button)
.forEach(btn->btn.setOnAction(ea->btn.setDisable(true)));
在这两种情况下,您还可以从并行操作中获益:
IntStream.range(0,10).parallel()
.forEach(i->button[i].setOnAction(ea->button[i].setDisable(true)));
Stream.of(button).parallel()
.forEach(btn->btn.setOnAction(ea->btn.setDisable(true)));
使用 Event
获取来源 Node
。
for(int i = 0; i < button.length; i++)
{
button[i].setOnAction(event ->{
((Button)event.getSource()).setDisable(true);
});
}
Lambda 表达式实际上类似于在流上运行的匿名方法。为了避免任何不安全的操作,Java 规定在 lambda 表达式中不能访问任何可以修改的外部变量。
为了解决这个问题,
final int index=button[i]; 并在 lambda 表达式中使用索引而不是 i。
你说如果按下按钮,但在你的示例中,列表中的所有按钮都将被禁用。尝试为每个按钮关联一个侦听器,而不是仅仅禁用它。
对于逻辑,你的意思是这样的:
Arrays.asList(buttons).forEach(
button -> button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
}
}));
我也喜欢 Sedrick 的回答,但你必须在循环中添加一个动作侦听器。