I have following class:
public class Item{
private String name;
//setter getter
}
And Collection of items. I would like to get name of last Item in Collection. To do that I simply iterate over all collection and use last. The problem is I dont know why it forces me to use one element String array.
Why do I have to use:
String[] lastName = {""};
items.forEach(item -> lastName[0] = item.getName());
System.out.println(lastname[0]);
instead of:
final String lastName;
items.forEach(item -> lastName = item.getName());
System.out.println(lastname);
The local variables that a lambda expression may use are referred to as “effectively final”. An effectively final variable is one whose value doesn't change after it's first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error.
Forcing the variable to be final avoids giving the impression that incrementing start inside the lambda could actually modify the start method parameter.
The effectively final variables refer to local variables that are not declared final explicitly and can't be changed once initialized. A lambda expression can use a local variable in outer scopes only if they are effectively final.
A lambda expression can't define any new scope as an anonymous inner class does, so we can't declare a local variable with the same which is already declared in the enclosing scope of a lambda expression. Inside lambda expression, we can't assign any value to some local variable declared outside the lambda expression.
final
actually means that you must assign it once (and only once, as guaranteed by compile time analysis). For instance, the following code is invalid:
final String lastName;
List<Item> items = new ArrayList<Item>();
items.add(new Item("only one element"));
for (Item item:items) lastName = item.getName();
In your second lambda expression, the consumer assigns to lastName
, which has been declared as final
:
final String lastName;
items.forEach(item -> lastName = item.getName());
Since variables referenced in a lambda expression must be effectively final (i.e. such that the final
keyword may be added to its definition), deleting the final
keyword in the declaration of lastName
doesn't help: here lastName
is effectively final, and the consumer assigns to an effectively final variable.
String lastName;
items.forEach(item -> lastName = item.getName());
On the other hand, in your first expression the effectively final variable is lastName
, which is an array. Then you can do:
String[] lastName = {""};
items.forEach(item -> lastName[0] = item.getName());
Because here you are not assigning to the effectively final variable, but only modifying its elements (remember that what is constant is the reference, not its values), as it is also the case in the following example:
final String[] lastName = new String[1];
lastName[0]="foo";
You can not make lastName
a String
, because a local variable that's used in a lambda (or anonymous inner class) must be (effectively) final
(see here), i.e. you can not overwrite it in each execution of the lambda in the .forEach
loop. When using an array (or some other wrapper object), you do not assign a new value to that variable, but only change some aspect of it, thus it can be final.
Alternatively, you could use reduce
to skip to the last item:
String lastName = items.stream().reduce((a, b) -> b).get().getName();
Or, as noted in comments, skip
the first n-1 elements and take the first after that:
String last = items.stream().skip(items.size() - 1).findFirst().get().getName();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With