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 llines, 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.)

Leave a Comment