2009-03-19 45 views
26

Tôi không hiểu tại sao điều này lại biên dịch. f() và g() được nhìn thấy từ các lớp bên trong, mặc dù là riêng tư. Họ được đối xử đặc biệt đặc biệt bởi vì họ là những lớp bên trong?Tại sao các lớp bên trong làm cho các phương thức riêng có thể truy cập?

Nếu A và B không phải là các lớp tĩnh, nó vẫn như cũ.

class NotPrivate { 
    private static class A { 
     private void f() { 
      new B().g(); 
     } 
    } 

    private static class B { 
     private void g() { 
      new A().f(); 
     } 
    } 
} 

Trả lời

26

(Edit: mở rộng về việc trả lời trả lời một số ý kiến)

Trình biên dịch có các lớp bên trong và biến chúng thành các lớp đầu cấp. Vì các phương thức riêng chỉ có sẵn cho lớp bên trong nên trình biên dịch phải thêm các phương thức "tổng hợp" mới có quyền truy cập cấp gói để các lớp cấp cao nhất có quyền truy cập vào nó.

Something như thế này ($ người được bổ sung bởi trình biên dịch):

class A 
{ 
    private void f() 
    { 
     final B b; 

     b = new B(); 

     // call changed by the compiler 
     b.$g(); 
    } 

    // method generated by the compiler - visible by classes in the same package 
    void $f() 
    { 
     f(); 
    } 
} 

class B 
{ 
    private void g() 
    { 
     final A a; 

     a = new A(); 

     // call changed by the compiler 
     a.$f(); 
    } 

    // method generated by the compiler - visible by classes in the same package 
    void $g() 
    { 
     g(); 
    } 
} 

lớp Non-tĩnh là như nhau, nhưng họ có sự bổ sung của một tham chiếu đến các lớp bên ngoài để các phương pháp có thể được gọi vào nó.

Lý do Java làm theo cách này là họ không muốn yêu cầu thay đổi VM để hỗ trợ các lớp bên trong, vì vậy tất cả các thay đổi phải ở cấp trình biên dịch.

Trình biên dịch lấy lớp bên trong và biến nó thành lớp cấp cao nhất (do đó, ở cấp VM không có thứ gì như lớp bên trong). Trình biên dịch sau đó cũng phải tạo ra các phương thức "chuyển tiếp" mới. Chúng được thực hiện ở cấp gói (không công khai) để đảm bảo rằng chỉ các lớp trong cùng một gói có thể truy cập chúng. Trình biên dịch cũng đã cập nhật các cuộc gọi phương thức tới các phương thức riêng cho các phương thức "forwarding" được tạo ra.

Bạn có thể tránh việc trình biên dịch tạo phương thức mà tôi khai báo các phương thức là "gói" (không có công khai, riêng tư và được bảo vệ). Nhược điểm đó là bất kỳ lớp nào trong gói có thể gọi các phương thức.

+1

vì vậy, bạn khai báo một phương thức riêng tư và trình biên dịch biến nó thành công cộng thay vì tạo ra một lỗi? – cbrulak

+0

cũng không công khai nó là gói. Và, vâng, đó là những gì nó làm. Điều này là do VM không có kiến ​​thức về các lớp bên trong ... do đó trình biên dịch thực hiện tất cả công việc thay vì làm cho VM thực hiện những việc đặc biệt. – TofuBeer

+0

gói là lớp có chứa lớp bên trong? – cbrulak

3

Java biên dịch trong số người truy cập đặc biệt có $ trong số đó. Vì vậy, bạn không thể viết Java để truy cập các phương thức riêng tư. Giải thích ở đây:

http://www.retrologic.com/innerclasses.doc7.html

Có thêm một hạng mục của các thành viên biên dịch tạo ra. Một thành viên riêng của một lớp C có thể được sử dụng bởi lớp D khác, nếu một lớp bao quanh lớp kia, hoặc nếu chúng được bao quanh bởi một lớp chung. Vì máy ảo không biết về kiểu phân nhóm này, trình biên dịch tạo ra một giao thức cục bộ của các phương thức truy cập trong C để cho phép D đọc, viết, hoặc gọi thành viên m. Những phương thức này có tên của biểu mẫu truy cập $ 0, truy cập $ 1, vv Chúng không bao giờ công khai. Các phương thức truy cập là duy nhất ở chỗ chúng có thể được thêm vào các lớp bao quanh, không chỉ các lớp bên trong.

13

Tôi nghĩ this quote tiền nó lên độc đáo:

... lớp bên trong có thể truy cập vào tất cả các thành viên của lớp tuyên bố, ngay cả các thành viên tư nhân. Thực ra, bản thân lớp bên trong được cho là thành viên của lớp; do đó, tuân theo các quy tắc về kỹ thuật hướng đối tượng, nó phải có quyền truy cập vào tất cả các thành viên của lớp.

Và sau đó, vì cả hai lớp bên trong thực sự chỉ là một phần của lớp chứa, nên chúng cũng có thể truy cập vào các thành viên riêng tư khác.

+0

"Thực tế, khi lớp của bạn được biên dịch tất cả các lớp bên trong thực sự 'bị thu gọn' để trở thành một phần của lớp chứa." - Ý anh là sao? –

+0

Tôi khá chắc chắn điều đó không đúng. Thông thường các lớp của biểu mẫu Outer $ Inner được tạo ra. Bạn không thể thực sự làm những gì bạn nói bởi vì bạn có thể có số lượng đối tượng khác nhau của lớp bên trong và bên ngoài. –

+0

Bạn nói đúng, đó là một chút sai lầm. Đã xóa phần đó khỏi câu trả lời của tôi. –

0

Nó được yêu cầu phải làm việc bằng cách xác định, ví dụ: Thông số ngôn ngữ Java nói như vậy:

6.6.1 Xác định khả năng truy cập (ít nhất là từ JLS6)

"Nếu không, nếu thành viên hoặc người xây dựng được khai báo riêng, thì quyền truy cập được phép nếu và chỉ khi nó xảy ra trong nội dung của lớp cấp cao nhất (§7.6) bao quanh tuyên bố của thành viên hoặc nhà xây dựng. "

I.e. "phạm vi truy cập" của một thành viên riêng tư là: ở mọi nơi trong phạm vi từ vựng của cơ thể cấp cao nhất.

Điều đó có nghĩa là: tất cả các thành viên riêng được xác định trong lớp cơ thể của lớp ngoài cùng có thể được truy cập từ mọi nơi khác trong lớp học này.

Ví dụ, phương pháp riêng của lớp bên trong có thể được truy cập từ các phương thức của lớp bên ngoài hoặc từ bất kỳ phương thức nào của lớp bên trong khác của lớp bên ngoài.

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