Problem binding a boolean property using a lambda [Java - JavaFX] [duplicate] - java

I want to be able to do something like this:
for(i = 0; i < 10; i++) {
//if any button in the array is pressed, disable it.
button[i].setOnAction( ae -> { button[i].setDisable(true) } );
}
However, I get a error saying "local variables referenced from a lambda expression must be final or effectively final". How might I still do something like the code above (if it is even possible)? If it can't be done, what should be done instead to get a similar result?

As the error message says, local variables referenced from a lambda expression must be final or effectively final ("effectively final" meaning the compiler can make it final for you).
Simple workaround:
for(i = 0; i < 10; i++) {
final int ii = i;
button[i].setOnAction( ae -> { button[ii].setDisable(true) } );
}

Since you are using lambdas, you can benefit also from other features of Java 8, like streams.
For instance, IntStream:
A sequence of primitive int-valued elements supporting sequential and parallel aggregate operations. This is the int primitive specialization of Stream.
can be used to replace the for loop:
IntStream.range(0,10).forEach(i->{...});
so now you have an index that can be used to your purpose:
IntStream.range(0,10)
.forEach(i->button[i].setOnAction(ea->button[i].setDisable(true)));
Also you can generate a stream from an array:
Stream.of(button).forEach(btn->{...});
In this case you won't have an index, so as #shmosel suggests, you can use the source of the event:
Stream.of(button)
.forEach(btn->btn.setOnAction(ea->((Button)ea.getSource()).setDisable(true)));
EDIT
As #James_D suggests, there's no need of downcasting here:
Stream.of(button)
.forEach(btn->btn.setOnAction(ea->btn.setDisable(true)));
In both cases, you can also benefit from parallel operations:
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)));

Use the Event to get the source Node.
for(int i = 0; i < button.length; i++)
{
button[i].setOnAction(event ->{
((Button)event.getSource()).setDisable(true);
});
}

Lambda expressions are effectively like an annonymous method which works on stream. In order to avoid any unsafe operations, Java has made that no external variables which can be modified, can be accessed in a lambda expression.
In order to work around it,
final int index=button[i];
And use index instead of i inside your lambda expression.

You say If the button is pressed, but in your example all the buttons in the list will be disabled. Try to associate a listener to each button rather than just disable it.
For the logic, do you mean something like that :
Arrays.asList(buttons).forEach(
button -> button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
}
}));
I Also like Sedrick's answer but you have to add an action listener inside the loop .

Related

for list iterator and has to manipulate by index

have a list of bean and I want to manipulate by its index and I tried below way, is there any other way of doing this which is easier and generic?
List<UserBean> resultBean = query.setFirstResult(offset).setMaxResults(limit).getResultList();
for (int i = 0; i < resultBean.size(); i++) {
resultBean.get(i).setChabi(encode(decyptChabi(resultBean.get(i).getChabi())));
}
As you can see from the Java Language Specification (JLS), 14.14, there are two kinds of for loops. The basic for loop, which uses an index, and the enhanced for loop, which doesn't.
You used the basic for loop but violated the DRY (Don't Repeat Yourself) principle in that you're calling resultBean.get(i) twice. To cure that, you can introduce a variable which makes the code much more readable:
for (int i = 0; i < resultBean.size(); i++) {
UserBean user = resultBean.get(i);
user.setChabi(encode(decryptChabi(user.getChabi())));
}
In your example, you don't even need the index variable, so you can replace the basic for loop with an enhanced for loop which would be even more concise:
for (UserBean user : resultBean) {
user.setChabi(encode(decryptChabi(user.getChabi())));
}
Whatever you do, prefer code that is easy to read over code that is fast/easy to write.

Is it possible to declare a variable within a Java while conditional?

In Java it is possible to declare a variable in the initialization part of a for-loop:
for ( int i=0; i < 10; i++) {
// do something (with i)
}
But with the while statement this seems not to be possible.
Quite often I see code like this, when the conditional for the while loop needs to be updated after every iteration:
List<Object> processables = retrieveProcessableItems(..); // initial fetch
while (processables.size() > 0) {
// process items
processables = retrieveProcessableItems(..); // update
}
Here on stackoverflow I found at least a solution to prevent the duplicate code of fetching the processable items:
List<Object> processables;
while ((processables = retrieveProcessableItems(..)).size() > 0) {
// process items
}
But the variable still has to be declared outside the while-loop.
So, as I want to keep my variable scopes clean, is it possible to declare a variable within the while conditional, or is there some other solution for such cases?
You can write a while loop using a for loop:
while (condition) { ... }
is the same as
for (; condition; ) { ... }
since all three bits in the brackets of the basic for statement declaration are optional:
BasicForStatement:
for ( [ForInit] ; [Expression] ; [ForUpdate] ) Statement
Similarly, you can just rewrite your while loop as a for loop:
for (List<Object> processables;
(processables = retrieveProcessableItems(..)).size() > 0;) {
// ... Process items.
}
Note that some static analysis tools (e.g. eclipse 4.5) might demand that an initial value is assigned to processables, e.g. List<Object> processables = null. This is incorrect, according to JLS; my version of javac does not complain if the variable is left initially unassigned.
No it's not possible.
It doesn't really make too much sense either: unlike a for loop where you can set up the initial state of the "looping variable", in a while loop you test the value of an existing variable, akin to the conditional check of the for loop.
Of course, if you're concerned about variables "leaking" into other parts of your code, you could enclose the whole thing in an extra scope block:
{
/*declare variable here*/
while(...){...}
}
Alternatively, convert the while loop into a for loop.
Make a do/while:
String value;
do {
value = getValue();
...your processing
} while (value != null && !value.isEmpty());

use increment inside lambda in java

I have a block like this
int i = 0;
shaders.forEach((WrappedShader shader) ->
{
int shader_id = glCreateShader(shader.getShader_type());
glShaderSource(shader_id, shader.getShader_source());
glCompileShader(shader_id);
glAttachShader(shader_program, shader_id);
shader_ids[i++] = shader_id;
});
But how to have increment inside lambda? Currently it asks to set the variable i as final and do not change it.
I know I can rewrite it to for loop but I'd like to use lambda.
Ideally, for functional program you should have pure functions with no side effects. In this case, you have side effects but you could do is
int[] sharedIds = shaders.stream().mapToInt((WrappedShader shader) -> {
int shader_id = glCreateShader(shader.getShader_type());
glShaderSource(shader_id, shader.getShader_source());
glCompileShader(shader_id);
glAttachShader(shader_program, shader_id);
return shared_id;
}).toArray();

java: Is it possible to set a lambda expression for an array of Buttons is a for loop? If so how?

I want to be able to do something like this:
for(i = 0; i < 10; i++) {
//if any button in the array is pressed, disable it.
button[i].setOnAction( ae -> { button[i].setDisable(true) } );
}
However, I get a error saying "local variables referenced from a lambda expression must be final or effectively final". How might I still do something like the code above (if it is even possible)? If it can't be done, what should be done instead to get a similar result?
As the error message says, local variables referenced from a lambda expression must be final or effectively final ("effectively final" meaning the compiler can make it final for you).
Simple workaround:
for(i = 0; i < 10; i++) {
final int ii = i;
button[i].setOnAction( ae -> { button[ii].setDisable(true) } );
}
Since you are using lambdas, you can benefit also from other features of Java 8, like streams.
For instance, IntStream:
A sequence of primitive int-valued elements supporting sequential and parallel aggregate operations. This is the int primitive specialization of Stream.
can be used to replace the for loop:
IntStream.range(0,10).forEach(i->{...});
so now you have an index that can be used to your purpose:
IntStream.range(0,10)
.forEach(i->button[i].setOnAction(ea->button[i].setDisable(true)));
Also you can generate a stream from an array:
Stream.of(button).forEach(btn->{...});
In this case you won't have an index, so as #shmosel suggests, you can use the source of the event:
Stream.of(button)
.forEach(btn->btn.setOnAction(ea->((Button)ea.getSource()).setDisable(true)));
EDIT
As #James_D suggests, there's no need of downcasting here:
Stream.of(button)
.forEach(btn->btn.setOnAction(ea->btn.setDisable(true)));
In both cases, you can also benefit from parallel operations:
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)));
Use the Event to get the source Node.
for(int i = 0; i < button.length; i++)
{
button[i].setOnAction(event ->{
((Button)event.getSource()).setDisable(true);
});
}
Lambda expressions are effectively like an annonymous method which works on stream. In order to avoid any unsafe operations, Java has made that no external variables which can be modified, can be accessed in a lambda expression.
In order to work around it,
final int index=button[i];
And use index instead of i inside your lambda expression.
You say If the button is pressed, but in your example all the buttons in the list will be disabled. Try to associate a listener to each button rather than just disable it.
For the logic, do you mean something like that :
Arrays.asList(buttons).forEach(
button -> button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
}
}));
I Also like Sedrick's answer but you have to add an action listener inside the loop .

using { } after semicolon [duplicate]

This question already has answers here:
Anonymous code blocks in Java
(11 answers)
Closed 9 years ago.
In an example of android code given in a book regarding action bar, the sample given is as the following:
MenuItem menu1 = menu.add(0, 0, 0, "Item 1");
{
menu1.setIcon(R.drawable.ic_launcher);
menu1.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
How is using curly braces after a semi-colon possible? There is clearly some concept that I do not understand right here.
They are completely optional in this case and have no side-effect at all. In your example it sole serves to purpose of making the code more readable by intending the property assignment which belong to the control. You could as well do it without the braces. But if you'd use a tool to reformat your code, the indentation is likely gone.
However, if you have a Method and you put {} in there, you can create a new variable scope:
void someMethod() {
{
int x = 1;
}
// no x defined here
{
// no x here, so we may define a new one
string x = "Hello";
}
}
You can start a new scope anywhere in a Method where you can start a statement (a variable declaration, a method call, a loop etc.)
Note: When you have for example an if-statement you also create a new variable scope with that braces.
void someMethod() {
if (someThing) {
int x = 1;
}
// no x defined here
if (somethingElse) {
// no x here, so we may define a new one
string x = "Hello";
}
}
The same is true for while, for, try, catch and so on. If you think about it even the braces of a method body work in that way: they create a new scope, which is a "layer" on top of the class-scope.
It's called anonymous code blocks, they are supposed to restrict the variable scope.
Those are Initialization Blocks.
I don't think this is the correct usage of initialization block.
Apart from the example you have produced, these blocks are just used for initialization purpose. Click Here for detailed view.

Categories

Resources