local variables referenced from a lambda expression must be final or effectively final

You can just copy the value of readln2 into a final variable:

    final String labelText = readln2 ;
    Button button = new Button("Click the Button");
    button.setOnAction(e -> l.setText(labelText));

If you want to grab a new random line each time, you can either cache the lines of interest and select a random one in the event handler:

Button button = new Button("Click the button");
Label l = new Label();
try {
    List<String> lines = Files.lines(Paths.get("/temp/mantra.txt"))
        .skip(low)
        .limit(high - low)
        .collect(Collectors.toList());
    Random rng = new Random();
    button.setOnAction(evt -> l.setText(lines.get(rng.nextInt(lines.size()))));
} catch (IOException exc) {
    exc.printStackTrace();
}
// ...

Or you could just re-read the file in the event handler. The first technique is (much) faster but could consume a lot of memory; the second doesn't store any of the file contents in memory but reads a file each time the button is pressed, which could make the UI unresponsive.

The error you got basically tells you what was wrong: the only local variables you can access from inside a lambda expression are either final (declared final, which means they must be assigned a value exactly once) or "effectively final" (which basically means you could make them final without any other changes to the code).

Your code fails to compile because readln2 is assigned a value multiple times (inside a loop), so it cannot be declared final. Thus you can't access it in a lambda expression. In the code above, the only variables accessed in the lambda are l, lines, and rng, which are all "effectively final` as they are assigned a value exactly once. (You can declare them final and the code would still compile.)


The error you encountered means that every variable that you access inside a lambda expressions body has to be final or effectively final. For the difference, see this answer here: Difference between final and effectively final

The problem in your code is the following variable

String readln2 = null;

The variable gets declared and assigned later on, the compiler can not detect if it gets assigned once or multiple times, so it is not effectively final.

The easiest way to solve this is to use a wrapper object, in this case a StringProperty instead of a String. This wrapper gets assigned only once and thus is effectively final:

StringProperty readln2 = new SimpleStringProperty();
readln2.set(in.readLine());
button.setOnAction(e -> l.setText(readln2.get()));

I shortened the code to show only the relevant parts..

Tags:

Java

Lambda