2010-07-14 99 views
9

如何在此匿名內部子類中傳遞給此方法的值爲userIdJava:使用局部變量的匿名內部類

public void doStuff(String userID) { 
    doOtherStuff(userID, new SuccessDelegate() { 
     @Override 
     public void onSuccess() { 
      Log.e(TAG, "Called delegate!!!! "+ userID); 
     } 
    }); 
} 

我得到這個錯誤:

Cannot refer to a non-final variable userID inside an inner class defined in a different method

我敢肯定,因爲它是一個未知值的變量,我不能將其指定爲final。我聽說這個語法在某種程度上確實保留了範圍,所以我認爲必須有一個我還不太瞭解的語法技巧。

+0

太糟糕了,java需要在這裏做決定。編譯器明顯知道局部變量沒有改變,所以不應該要求編碼器確認。如果匿名類寫入局部變量,編譯警告就足夠了。糟糕的壞設計選擇。 – irreputable 2010-07-14 23:24:13

+0

可能的重複[爲什麼只有最後一個變量可以在匿名類中訪問?](http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class) – vaxquis 2015-07-01 14:23:45

回答

7

當然,你可以將它作爲最後的 - 只是把該關鍵字在參數聲明:

public void doStuff(final String userID) { 
    ... 

我不知道你的意思是它是一個未知值的變量是什麼;所有最後的意思是,一旦賦值給變量,它不能是re -assigned。既然你沒有改變你的方法中的用戶標識的值,在這種情況下最終決定是沒有問題的。

+0

阿好了。我想我真的不明白'final'聲明是如何工作的。謝謝。 – 2010-07-14 22:31:59

1

用什麼使它final

public void doStuff (final String userID) 
1

問題出在聲明

public void doStuff(final String userID) 

的值必須是最終使編譯器可以確保它不會改變的方法。這意味着編譯器可以隨時將值綁定到內部類,而不必擔心更新。

代碼中的值沒有改變,所以這是一個安全的改變。

10

正如其他人在這裏所說的,局部變量必須是最終才能被內部類訪問。

這裏是(基本上)這是爲什麼?如果你寫了下面的代碼(長的答案,但是,在底部,你可以得到短版:-):

class Main 
{ 
    private static interface Foo 
    { 
     void bar(); 
    } 

    public static void main(String[] args) 
    { 
     final int x; 
     Foo foo; 

     x = 42; 
     foo = new Foo() 
     { 
      public void bar() 
      { 
       System.out.println(x); 
      } 
     }; 

     foo.bar(); 
    } 
} 

編譯大致翻譯這樣的:

class Main 
{ 
    private static interface Foo 
    { 
     void bar(); 
    } 

    public static void main(String[] args) 
    { 
     final int x; 
     Foo foo; 

     x = 42; 

     class $1 
      implements Foo 
     { 
      public void bar() 
      { 
       System.out.println(x); 
      } 
     } 

     foo = new $1(); 
     foo.bar(); 
    } 
} 

,然後這樣的:

class Main 
{ 
    private static interface Foo 
    { 
     void bar(); 
    } 

    public static void main(String[] args) 
    { 
     final int x; 
     Foo foo; 

     x = 42; 
     foo = new $1(x); 
     foo.bar(); 
    } 

    private static class $1 
     implements Foo 
    { 
     private final int x; 

     $1(int val) 
     { 
      x = val; 
     } 

     public void bar() 
     { 
      System.out.println(x); 
     } 
    } 
} 

終於到這一點:

class Main 
{ 
    public static void main(String[] args) 
    { 
     final int x; 
     Main$Foo foo; 

     x = 42; 
     foo = new Main$1(x); 
     foo.bar(); 
    } 
} 

interface Main$Foo 
{ 
    void bar(); 
} 

class Main$1 
    implements Main$Foo 
{ 
    private final int x; 

    Main$1(int val) 
    { 
     x = val; 
    } 

    public void bar() 
    { 
     System.out.println(x); 
    } 
} 

重要的是它將構造函數添加到$ 1的位置。試想一下,如果你能做到這一點:

class Main 
{ 
    private static interface Foo 
    { 
     void bar(); 
    } 

    public static void main(String[] args) 
    { 
     int x; 
     Foo foo; 

     x = 42; 
     foo = new Foo() 
     { 
      public void bar() 
      { 
       System.out.println(x); 
      } 
     }; 

     x = 1; 

     foo.bar(); 
    } 
} 

你會認爲foo.bar()將打印出1,但它實際上打印出42.通過要求局部變量是最終不能出現這種混亂的局面。

+0

問題:annon內部類只會自動複製它使用正確的'final'局部變量?不直接使用其他本地(最終或不)變量的副本?我問,因爲我擔心內存泄漏。 – 2012-10-11 19:45:06

1

在Java 8中,這已經改變了一點點。您現在可以訪問變量,最終有效地爲Oracle documentation(我的重點)的相關片段和示例:

However, starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. A variable or parameter whose value is never changed after it is initialized is effectively final. For example, suppose that the variable numberLength is not declared final, and you add the highlighted assignment statement in the PhoneNumber constructor:

PhoneNumber(String phoneNumber) { 
    numberLength = 7; // From Kobit: this would be the highlighted line 
    String currentNumber = phoneNumber.replaceAll(
     regularExpression, ""); 
    if (currentNumber.length() == numberLength) 
     formattedPhoneNumber = currentNumber; 
    else 
     formattedPhoneNumber = null; 
} 

Because of this assignment statement, the variable numberLength is not effectively final anymore. As a result, the Java compiler generates an error message similar to "local variables referenced from an inner class must be final or effectively final" where the inner class PhoneNumber tries to access the numberLength variable:

if (currentNumber.length() == numberLength) 

Starting in Java SE 8, if you declare the local class in a method, it can access the method's parameters. For example, you can define the following method in the PhoneNumber local class:

public void printOriginalNumbers() { 
    System.out.println("Original numbers are " + phoneNumber1 + 
     " and " + phoneNumber2); 
} 

The method printOriginalNumbers accesses the parameters phoneNumber1 and phoneNumber2 of the method validatePhoneNumber