Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

lambda

I have a JavaFX 8 program (for JavaFXPorts cross platfrom) pretty much framed to do what I want but came up one step short. The program reads a text file, counts the lines to establish a random range, picks a random number from that range and reads that line in for display.

The error is: local variables referenced from a lambda expression must be final or effectively final         button.setOnAction(e -> l.setText(readln2)); 

I am a bit new to java but is seems whether I use Lambda or not to have the next random line display in Label l, my button.setOnAction(e -> l.setText(readln2)); line is expecting a static value.

Any ideas how I can tweak what I have to simply make the next value of the var readln2 display each time I press the button on the screen?

Thanks in advance and here is my code:

String readln2 = null; in = new BufferedReader(new FileReader("/temp/mantra.txt")); long linecnt = in.lines().count(); int linenum = rand1.nextInt((int) (linecnt - Low)) + Low; try {     //open a bufferedReader to file      in = new BufferedReader(new FileReader("/temp/mantra.txt"));      while (linenum > 0) {         //read the next line until the specific line is found         readln2 = in.readLine();         linenum--;     }      in.close(); } catch (IOException e) {     System.out.println("There was a problem:" + e); }  Button button = new Button("Click the Button"); button.setOnAction(e -> l.setText(readln2)); //  error: local variables referenced from a lambda expression must be final or effectively final 
like image 329
Jeff Avatar asked Dec 21 '14 18:12

Jeff


1 Answers

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

like image 195
James_D Avatar answered Oct 06 '22 18:10

James_D