1

When I am working with optional class of java like below

Integer total = null;
Optional<Integer> b = Optional.of(new Integer(10));
b.ifPresent(b -> total =b);

The above code is not working(Error: java: local variables referenced from a lambda expression must be final or effectively final) but, when I use the AtomicInteger, It will work. Why this happens?

Optional<Integer> b = Optional.of(new Integer(10));
AtomicInteger total = new AtomicInteger();
b.ifPresent(b -> total.set(b));
  • 1
    you want to assign a value to wrapper which is immutable and within lambda? Why? – SMA Oct 28 '19 at 06:09

4 Answers4

4

You can do it like this:

Integer total = Optional.of(new Integer(10)).orElse(null);

And if the Optional value can be nullable then:

Integer total = Optional.ofNullable(new Integer(10)).orElse(null);

Optional.ofNullable will prevent NPE in case of null value.

The reason you're getting this error in the first example is that in lambda expression you're not allowed to change the reference of the local variables. That's why they need to be either declared final or effectively final.

And the reason the second example is working because here you're not changing the reference of the total variable. You're only updating its value using its set() method.

Mushif Ali Nawaz
  • 3,707
  • 3
  • 18
  • 31
3

In this case you are not just observing a difference between the two classes, but rather a difference in how you are using them.

b.ifPresent(b -> total =b);

This attempts to assign a new reference, a reference to a different Integer object, to b. This would neither be allowed with Integer, AtomicInteger or any other type.

b.ifPresent(b -> total.set(b));

This calls a method (set) in the existing AtomicInteger object. Calling a method from within a lambda is allowed for both Integer, AtomicInteger and all other classes. One important difference for your use case, though, is that Integer hasn’t got a method that allows you to change the value since the class is immutable.

What to do instead? See the good answer by Mushif Ali Nawaz.

PS If you want to know more about the difference between the two classes, see this question: What is the difference between Atomic Integer and Normal immutable Integer class in Java?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

You asked why the behaviour of Integer and AtomicInteger is different in your case. The reason really has nothing to do with AtomicInteger. Rather the difference is that Integer is immutable and, therefore, the only way of changing the value of a reference to an Integer is through assignment. But lambda expressions do not allow assignment to a local variable that is outside the scope of the expression.

There are several ways you could resolve this without using AtomicInteger:

  1. create your own MutableInteger that can be set inside the lambda
  2. make the Integer an object variable rather than local variable
  3. use orElse or the various alternatives to return the value rather than setting it inside ifPresent

Note also that you might consider OptionalInt rather than Optional<Integer>.

sprinter
  • 27,148
  • 6
  • 47
  • 78
0

They behave differently because you are doing two totally things.

In your first :

b.ifPresent(b -> total =b);

The variable “total” represents an area in memory that you are changing the value of. That is what is not allowed.

In the second :

b.ifPresent(b -> total.set(b));

The variable “total” represents an area in memory that is referencing (or pointing to) an instance of the class AtomicInteger. You are not changing that variable, but simply calling a method on it. Since “total” itself remains unchanged, it is what is termed “effectively final”, and so is allowed.

racraman
  • 4,988
  • 1
  • 16
  • 16