2010-05-10 33 views
20

Trong Java một lớp bên trong vô danh có thể tham khảo các biến trong nó là phạm vi địa phương:java thực thi lớp bên trong như thế nào?

public class A { 
    public void method() { 
     final int i = 0; 

     doStuff(new Action() { 
      public void doAction() { 
       Console.printf(i); // or whatever 
      } 
     }); 
    } 
} 

Câu hỏi của tôi là làm thế nào là điều này thực sự thực hiện? Làm thế nào để i truy cập vào việc thực hiện bên trong vô danh doAction và tại sao nó phải là final?

Trả lời

11

Trình biên dịch sẽ tự động tạo một hàm tạo cho lớp bên trong ẩn danh của bạn và chuyển biến cục bộ của bạn vào hàm tạo này.

Hàm tạo lưu giá trị này trong biến lớp (trường), cũng được đặt tên là i, sẽ được sử dụng bên trong "đóng cửa".

Tại sao nó phải là cuối cùng? Vâng chúng ta hãy cùng khám phá tình hình ở nơi nó không phải là:

public class A { 
    public void method() { 
     int i = 0; // note: this is WRONG code 

     doStuff(new Action() { 
      public void doAction() { 
       Console.printf(i); // or whatever 
      } 
     }); 

     i = 4; // A 
     // B 
     i = 5; // C 
    } 
} 

Trong tình huống Một lĩnh vực i của Action cũng cần phải được thay đổi, chúng ta hãy giả định này có thể: nó cần tham chiếu đến đối tượng Action.

Giả sử trong trường hợp B, trường hợp này là Action là Thu thập rác.

Hiện tại trong trường hợp C: cần một phiên bản Action để cập nhật biến lớp học của nó, nhưng giá trị được GC. Nó cần phải "biết" nó GCed, nhưng đó là khó khăn.Vì vậy, để giữ cho việc triển khai máy ảo đơn giản hơn, các nhà thiết kế ngôn ngữ Java đã nói rằng nó phải là cuối cùng sao cho VM không cần một cách để kiểm tra xem một đối tượng có biến mất hay không và đảm bảo rằng biến đó không phải là sửa đổi, và rằng VM hoặc trình biên dịch không phải giữ tham chiếu của tất cả các tập quán của biến bên trong các lớp bên trong vô danh và các cá thể của chúng.

+0

Thực ra, biến tổng hợp chứa bản sao của biến không được đặt tên là i. Tùy thuộc vào phiên bản của trình biên dịch bạn đang sử dụng, nó là "$ i" hoặc "+ i". –

15

Biến cục bộ (rõ ràng) không được chia sẻ giữa các phương pháp khác nhau như method()doAction() ở trên. Nhưng vì nó là cuối cùng, không có gì "xấu" có thể xảy ra trong trường hợp này, vì vậy ngôn ngữ vẫn cho phép nó. Trình biên dịch tuy nhiên, cần phải làm một cái gì đó thông minh về tình hình. Cho phép có một cái nhìn vào những gì javac sản xuất:

$ javap -v "A\$1"   # A$1 is the anonymous Action-class. 
... 
final int val$i; // A field to store the i-value in. 

final A this$0;  // A reference to the "enclosing" A-object. 

A$1(A, int); // created constructor of the anonymous class 
    Code: 
    Stack=2, Locals=3, Args_size=3 
    0: aload_0 
    1: aload_1 
    2: putfield #1; //Field this$0:LA; 
    5: aload_0 
    6: iload_2 
    7: putfield #2; //Field val$i:I 
    10: aload_0 
    11: invokespecial #3; //Method java/lang/Object."<init>":()V 
    14: return 
    ... 
public void doAction(); 
    Code: 
    Stack=2, Locals=1, Args_size=1 
    0: getstatiC#4; //Field java/lang/System.out:Ljava/io/PrintStream; 
    3: aload_0 
    4: getfield #2; //Field val$i:I 
    7: invokevirtual #5; //Method java/io/PrintStream.println:(I)V 
    10: return 

Điều này thực sự cho thấy rằng nó

  • quay biến i vào một lĩnh vực,
  • tạo một constructor cho lớp vô danh, mà chấp nhận một tham chiếu đối tượng A
  • mà sau này truy cập trong phương thức doAction().

(Một mặt lưu ý:. Tôi đã phải khởi tạo biến để new java.util.Random().nextInt() để ngăn chặn nó từ việc tối ưu hóa đi rất nhiều mã)


thảo luận tương tự ở đây

method local innerclasses accessing the local variables of the method

+2

Nó không có gì (nhiều) để làm với luồng. Nó chỉ là một tác dụng phụ. Đẹp java disasm, btw: cung cấp cho một số cái nhìn sâu sắc tuyệt vời vào trình biên dịch. – Pindatjuh

+0

Bạn nói đúng. Tôi sẽ sửa lại. Cảm ơn con trỏ. – aioobe

+0

@Pindatjuh, Cập nhật các disasm ... nhận ra nó tối ưu hóa rất nhiều mã như trình biên dịch nhận ra 'i' luôn luôn là 0. – aioobe

3

Ví dụ lớp cục bộ (lớp ẩn danh) phải duy trì một bản sao riêng biệt của biến, vì nó có thể tồn tại chức năng. Vì vậy, vì không có sự nhầm lẫn của hai biến có thể sửa đổi có cùng tên trong cùng một phạm vi, biến được buộc phải là cuối cùng.

Xem Java Final - an enduring mystery để biết thêm chi tiết.

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