2010-07-14 34 views
9

Làm cách nào tôi có thể nhận giá trị userId được chuyển đến phương thức này trong lớp con bên trong ẩn danh của tôi ở đây?Java: Lớp bên trong ẩn danh sử dụng biến cục bộ

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

tôi nhận được lỗi này:

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

Tôi chắc chắn rằng tôi không thể gán nó như thức vì nó là một biến với một giá trị không rõ. Tôi đã nghe nói rằng cú pháp này bảo toàn phạm vi theo một cách nào đó, vì vậy tôi nghĩ rằng phải có một cú pháp cú pháp mà tôi chưa biết.

+0

java quá xấu yêu cầu cuối cùng ở đây. rõ ràng là trình biên dịch rằng biến cục bộ không bị thay đổi, nó không nên yêu cầu người lập trình xác nhận điều đó. trong trường hợp lớp anon ghi vào biến cục bộ, một cảnh báo biên dịch là đủ. lựa chọn thiết kế xấu xấu. – irreputable

+0

có thể trùng lặp của [Tại sao chỉ các biến cuối cùng có thể truy cập trong lớp ẩn danh?] (Http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class) – vaxquis

Trả lời

7

Chắc chắn bạn có thể gán nó như là cuối cùng - chỉ cần đặt từ khóa đó trong tờ khai của tham số:

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

Tôi không chắc chắn những gì bạn có nghĩa là về nó là một biến với một giá trị không rõ; tất cả những phương tiện cuối cùng đó là khi giá trị được gán cho biến, nó không thể là re được gán. Vì bạn không thay đổi giá trị của userID trong phương thức của mình, nên không có vấn đề gì làm cho nó cuối cùng trong trường hợp này.

+0

Ah ok . Tôi đoán tôi đã không thực sự hiểu cách khai báo 'final' hoạt động như thế nào. Cảm ơn. –

1

vấn đề gì với việc nó final như trong

public void doStuff (final String userID) 
1

tuyên bố phương pháp

public void doStuff(final String userID) 

Giá trị cần phải được chính thức để trình biên dịch có thể chắc chắn nó không thay đổi. Điều này có nghĩa là trình biên dịch có thể liên kết giá trị với lớp bên trong bất kỳ lúc nào mà không phải lo lắng về các bản cập nhật.

Giá trị không thay đổi trong mã của bạn vì vậy đây là thay đổi an toàn.

10

Như mọi người khác ở đây đã nói, biến cục bộ phải là cuối cùng để được truy cập bởi một lớp bên trong.

Dưới đây là (cơ bản) tại sao đó là ... nếu bạn viết đoạn code sau (câu trả lời dài, nhưng, ở phía dưới, bạn có thể nhận được phiên bản ngắn :-):

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

trình biên dịch dịch nó gần như thế này:

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

và sau đó này:

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); 
     } 
    } 
} 

và cuối cùng là như thế này:

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); 
    } 
} 

Điều quan trọng là nơi nó thêm hàm tạo vào $ 1. Hãy tưởng tượng nếu bạn có thể làm điều này:

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

Bạn mong chờ rằng foo.bar() sẽ in ra 1 nhưng nó thực sự sẽ in ra 42. Bằng cách yêu cầu các biến địa phương được chính thức tình huống khó hiểu này không thể xảy ra.

+0

Câu hỏi: Lớp bên trong annon chỉ tạo bản sao tự động của các biến cục bộ 'final' mà nó sử dụng đúng? Không phải bản sao của các biến địa phương (cuối cùng hay không) khác mà nó không trực tiếp sử dụng? Tôi hỏi vì tôi lo ngại về rò rỉ bộ nhớ. –

1

Trong Java 8, điều này đã thay đổi một chút. Bây giờ, bạn có thể truy cập các biến số hiệu quả cuối cùng.Đoạn mã và ví dụ có liên quan từ Oracle documentation (phần nhấn mạnh của tôi):

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

Các vấn đề liên quan