2012-03-13 100 views
15

我有這樣的代碼:for循環中的最後一個計數器?

List<Runnable> r = new ArrayList<>(); 
    for(int i = 0; i < 10; i++) { 
     r.add(new Runnable() { 

      @Override 
      public void run() { 
       System.out.println(i); 
      } 
     }); 
    } 

這顯然不能編譯,因爲i將需要最終在匿名類中使用。但是我不能做出最後的決定,因爲事實並非如此。你會怎麼做?一個解決辦法是複製,但我認爲有可能是一個更好的辦法:

List<Runnable> r = new ArrayList<>(); 
    for(int i = 0; i < 10; i++) { 
     final int i_final = i; 
     r.add(new Runnable() { 

      @Override 
      public void run() { 
       System.out.println(i_final); 
      } 
     }); 
    } 

編輯只是要清楚,我這裏使用的一個Runnable的例子來說,真正的問題是有關匿名類,這可能是其他任何東西。

+4

我不認爲有更好的辦法... – thumbmunkeys 2012-03-13 11:20:32

+0

由於顯而易見的原因,循環計數器永遠無法進入最終狀態,所以我認爲您將值複製到最終變量中的方法是唯一的方法(但我對可能丟失的備選方案感興趣)。 – ftr 2012-03-13 11:21:59

回答

16

我認爲你的解決方案是最簡單的方法。

另一種選擇是要重構內部類的創建成一個工廠函數,它會爲你,那麼你的循環本身可能會是乾淨的,如:

List<Runnable> r = new ArrayList<>(); 
for(int i = 0; i < 10; i++) { 
    r.add(generateRunnablePrinter(i)); 
} 

,工廠功能可能只是聲明最終的參數:

private Runnable generateRunnablePrinter(final int value) { 
    return new Runnable() { 
     public void run() { 
      System.out.println(value); 
     } 
    }; 
} 

我喜歡這種重構的方法,因爲它使代碼更乾淨,也比較自我描述性的,也隱藏了所有的內部類管道。

隨機題外話:如果你考慮匿名內部類等同於關閉,然後generateRunnablePrinter實際上是一個高階函數。誰說過你不能做函數式編程在Java中:-)

+3

我認爲這是目前爲止最清潔的方式。 – assylias 2012-03-13 11:32:38

2

這是IntelliJ爲您做的修復。唯一的區別就是我會做

ExecutorService es = 
for(int i = 0; i < 10; i++) { 
    final int i_final = i; 
    es.execute(new Runnable() { 
1

(最佳小於-)替代:創建一個實現Runnable小的內部類:

class Printer implements Runnable { 
    private int index; 

    public Printer(int index) { 
     this.index = index; 
    } 

    public void run() { 
     System.out.println(index); 
    } 
} 

List<Runnable> r = new ArrayList<>(); 
for(int i = 0; i < 10; i++) { 
    r.add(new Printer(i)); 
} 
+0

夠公平 - 不能說它讓它更具可讀性! – assylias 2012-03-13 11:24:45

+0

@assylias:沒錯。通常情況下,我也會使用你的初始版本,因爲run方法中的代碼太小了...... – Tudor 2012-03-13 11:25:50

+1

@assylias,實際上非anon類[但是用方法體聲明]在非常重要的情況下顯着更好:類直方圖和堆棧痕跡。我儘量避免匿名的,如果我能幫到的話。 – bestsss 2012-08-07 16:14:22

0

恐怕比複製您的計數器沒有別的辦法到第二個最終變量並在你的匿名內部類中使用它。這是圍繞關閉主題的Java的「缺陷」之一,同時也是Groovy等兄弟語言的廣告優勢。

+1

這不是一個真正的缺陷:它會阻止用戶發生某些類型的錯誤。如果Java編譯器沒有執行最終的變量,那麼很多人可能(錯誤地)認爲它們可以更新局部變量,並且「閉包」會看到結果。而且你可以打賭,這會造成很多的錯誤和很多困惑的SO問題! – mikera 2012-03-13 11:27:55

0

看起來好像沒什麼問題。您使用循環變量的值匿名類和循環變量裏面顯然不能是最終的(因爲它的價值變化)。

創建一個新的最終局部變量是一個很好的解決方案。

1

您的解決方案並不錯。你可以做其他的事情,比如定義你自己的子類Runnable,並在構造函數或初始化塊中用i進行初始化,但在這種情況下,我認爲只會增加複雜性而沒有一個好的原因。

BTW:我認爲你的例子是syntehetic之一,在實踐中創造了新的Runnable只是打印整數似乎不是一個好主意。