0

I understand the concept of final / effectively final within lambda , it essentially protects from side effects. However why the following code snippet works? Is it also not violating this behaviour?

import java.util.stream.Stream;

public class Learn {
    int x = 0;
    Stream<Integer> numbers() {
        x = x +1;
        return Stream.iterate(0, i -> {
            int result = x + i;
            x = i;
            return result;
        });
    }
    public static void main(String[] args) {

        Learn l = new Learn();
        l.numbers()
                .skip(20) // Don't use the first 20
                .limit(10) // Then take 10 of them
                .forEach(System.out::println);
        System.out.println("=====================");
        System.out.println(l.x);


    }
}

75025
121393
196418
317811
514229
=====================
317811
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • 2
    Can you explain why you think the code shouldn't work? You say you "understand the concept", but evidently you don't... – Sweeper Feb 09 '23 at 07:37
  • 2
    `x` is a field, not a local variable, so the requirement for final/effectively final doesn't apply. Also, the rule doesn't exist to _"protect[s] from side effects"_, it has to do with the lifetime of local variables, which can be shorter than the lambda created in a method, so Java **copies** the variable to the lambda. The final/effectively final rule is there so you don't get weird behaviour (or weird expectations about behaviour). – Mark Rotteveel Feb 09 '23 at 07:39
  • IIRC the Java Language Specification doesn't actually specify that it must copy the variable just that it captures the variable, but AFAIK the capture is currently achieved by copying, because with the final/effectively final rule that is the simplest way to do so. – Mark Rotteveel Feb 09 '23 at 07:46
  • If you meant that `x = i;` violates the requirement, I think of that as `Learn.this.x = i;` (it’s perfectly equivalent). Here `Learn.this` is considered final, which suffices to fulfil the requirement. You are allowed to modify the state of an object that you can reach over a final or effectively final reference. This is also why the usual tricks of using an `AtomicInteger` or an `int[1]` array work. – Ole V.V. Feb 09 '23 at 08:31
  • @OleV.V Thanks for the clarification . Yes that makes sense. Related question .. not having the constraint of final / effectively final for local variable ...what kind of complexity /ambiguity can arise – Amit Sinha Feb 09 '23 at 08:47
  • One case I can think of: you may pass the lambda to someone who executes it long after the method where it came from has returned. In that case the local variable that the lambda refers to does not exist anymore. By making sure that the local variable is effectively final, I suppose you can just pass its constant value along with the lambda. – Ole V.V. Feb 09 '23 at 13:04

0 Answers0