2014-12-21 221 views
13

我有一個JavaFX 8程序(用於JavaFXPorts交叉平臺)非常框架以做我想做的事,但想出了一步。該程序讀取一個文本文件,對行進行計數以建立一個隨機範圍,從該範圍中選取一個隨機數並讀取該行以進行顯示。從lambda表達式引用的局部變量必須是最終的或有效的最終

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

我有點新到Java,但似乎我是否使用LAMBDA與否有一個隨機行顯示在Label l,我button.setOnAction(e -> l.setText(readln2));行期待一個靜態值。

任何想法如何我可以調整什麼,我只需要每次按下屏幕上的按鈕時var readln2顯示的下一個值?

在此先感謝這裏是我的代碼:

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 
+0

解決此問題的最簡單方法是使用SimpleStringProperty而不是String來存儲'readln2'。 – eckig

+0

謝謝。你能否詳細說明一下。從看它我不知道如何使它與我正在閱讀/使用外部文件的方式兼容。 – Jeff

+0

在我看來,這個問題應該被削減到4行代碼。好吧,15行 - 它的Java畢竟! ;-)也許在這次減少練習中,答案會讓海報本身變得清晰。 –

回答

7

可以的readln2值只是複製到final變量:

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

如果你想每次都搶一個新的隨機線,您可以緩存感興趣的線,並選擇一個隨機事件處理程序:

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(); 
} 
// ... 

或者您可以重新讀取事件處理程序中的文件。第一種技術(更快)但可能消耗大量內存;第二個不會將任何文件內容存儲在內存中,但每次按下按鈕時都會讀取一個文件,這可能會導致UI無響應。

你有基本的錯誤告訴你什麼是錯的:你可以從一個lambda表達式中訪問的唯一的局部變量或者是final(聲明final,這意味着它們必須被分配一個值僅一次)或「有效地最終」 (這基本上意味着你可以在沒有對代碼進行任何其他更改的情況下使它們最終)。

您的代碼無法編譯,因爲readln2被賦值多次(在一個循環中),所以它不能被聲明爲final。因此你不能在lambda表達式中訪問它。在上面的代碼中,lambda中訪問的唯一變量是llinesrng,它們都是「有效最終」的,因爲它們只被賦值一次。(你可以聲明他們最終和代碼仍然編譯。)

+0

James_D。這也適用於我上面提到的警告,我也想辦法讓按鈕點擊事件也提供一個新的隨機行,每次我點擊。我可能只需要重新運行按鈕或移動一些按鈕,但是。作爲Java的新手,即使這也需要考慮。當我可以的時候會投你的答案。 – Jeff

+0

James,謝謝。您添加的新隨機線代碼塊看起來就像我所需要的。這個解釋也起了作用。每次隨機線路似乎都可以正常運行在Windows上的測試文件中。我會通過JavaFXPorts在Android上使用它(使用/storage/emulated/0/temp/mantra.txt作爲路徑),因爲它掛在我的第一次嘗試上。無論如何,我真的很感激它。 – Jeff

+1

我很好奇*爲什麼* Java強制執行此操作。這似乎與Java處理關閉的常規方式有所不同。 –

0

您遇到的錯誤意味着每次你一lambda表達式體內訪問的變量必須是最後的或有效的決賽。對於差異,看到這個答案在這裏:Difference between final and effectively final

在你的代碼的問題是以下變量

String readln2 = null; 

的變量被宣佈和分配以後,如果它被分配一次,編譯器無法檢測或多次,所以它不是最終的。

解決此問題的最簡單方法是使用包裝對象,在本例中爲StringProperty而不是String。此包裝被分配只有一次,因此是有效的決賽:

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

我縮短了代碼,只顯示相關部分..

+0

非常感謝eckig。我是拙劣的set/get語法。現在我只需要弄清楚每次點擊時如何使用新的隨機線讓按鈕循環。雖然分開的問題。我試圖投票,但我對Java很新,我還沒有名聲。我會盡快回來投票。再次感謝。對此,我真的非常感激。 – Jeff

1

我經常通過外部對象插入接口實現了這種方式: 1.創建一些對象持有人, 2.將該物體支架與一些期望的狀態, 3.更改對象保存器中的內部變量, 4.獲取這些變量並使用它們。

這裏是從Vaadin一個例子:

Object holder : 
    public class ObjectHolder<T> { 
    private T obj; 
    public ObjectHolder(T obj) { 
     this.obj = obj; 
    } 
    public T get() { 
     return obj; 
    } 
    public void set(T obj) { 
     this.obj = obj; 
    } 
} 

我想通過這樣外部定義的按鈕標題:

String[] bCaption = new String[]{"Start", "Stop", "Restart", "Status"}; 
String[] commOpt = bCaption; 

接着,我有個for循環中,並且希望動態地創建按鈕,並傳遞如下數值:

for (Integer i = 0; i < bCaption.length; i++) { 
    ObjectHolder<Integer> indeks = new ObjectHolder<>(i); 
    b[i] = new Button(bCaption[i], 
     (Button.ClickEvent e) -> { 
      remoteCommand.execute(
       cred, 
       adresaServera, 
       comm + " " + commOpt[indeks.get()].toLowerCase() 
      ); 
     } 
     ); 

     b[i].setWidth(70, Unit.PIXELS); 
     commandHL.addComponent(b[i]); 
     commandHL.setComponentAlignment(b[i], Alignment.MIDDLE_CENTER); 
    } 

希望這會有所幫助..

相關問題