2010-07-14 110 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