2009-03-11 35 views
41

Tôi không tưởng tượng rằng tôi sẽ gặp phải cú pháp hoàn toàn mới trong Java nữa ở giai đoạn này, nhưng lo and behold một cái gì đó, tôi chỉ gặp phải:cú pháp Strange cho instantiating một lớp bên trong

Bối cảnh chính xác và những gì mã dưới đây nên làm là khá không liên quan - nó chỉ có để cung cấp cho một số loại bối cảnh.

Tôi đang cố gắng để tổng hợp tạo ra một sự kiện trong lĩnh vực CNTT Mill Toolkit, vì vậy tôi đã viết như thế này của dòng:

buttonClick(new Button.ClickEvent(button)); 

Nhưng, Eclipse mang lại cho tôi thông báo lỗi sau:

Không thể truy cập thể hiện kèm theo loại Nút. Phải đủ điều kiện phân bổ với một thể hiện kèm theo của Button type (ví dụ x.new A() trong đó x là một thể hiện của Button).

Khi tôi viết lại những dòng trên như sau, nó không phàn nàn nữa:

buttonClick(button.new ClickEvent(button)); // button instanceof Button 

Vì vậy, câu hỏi của tôi là: không cú pháp sau có ý nghĩa gì, chính xác, và tại sao không đoạn đầu tiên hoạt động? Java phàn nàn về điều gì và nó làm gì trong phiên bản thứ hai?

Thông tin cơ bản: Cả hai ButtonButton.ClickEvent là các lớp công khai không trừu tượng.

+0

Thú vị câu hỏi, nhưng tiêu đề có lẽ có thể phức tạp hơn. – mafu

+0

Bạn có thể tự do đề xuất. Tôi không biết phải gọi cú pháp vào thời điểm nào, vì vậy tựa đề vẫn còn, thật không may, là lạ. –

Trả lời

69

lớp Inner (như Button.ClickEvent) cần một tham chiếu đến một thể hiện của lớp bên ngoài (Button).

Cú pháp đó tạo một phiên bản mới Button.ClickEvent với tham chiếu lớp ngoài được đặt thành giá trị button.

Dưới đây là một ví dụ - bỏ qua những thiếu đóng gói vv, nó chỉ nhằm mục đích minh họa:

class Outer 
{ 
    String name; 

    class Inner 
    { 
     void sayHi() 
     { 
      System.out.println("Outer name = " + name); 
     } 
    } 
} 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     Outer outer = new Outer(); 
     outer.name = "Fred"; 

     Outer.Inner inner = outer.new Inner(); 
     inner.sayHi(); 
    } 
} 

Xem section 8.1.3 of the spec để biết thêm về các lớp bên trong và các trường hợp kèm theo.

+7

Nếu lớp bên trong không cần tham chiếu đến lớp bên ngoài để hoạt động, bạn có thể loại bỏ yêu cầu này bằng cách sử dụng từ khóa tĩnh trong khai báo lớp bên trong - lớp tĩnh Bên trong trong ví dụ trên - mặc dù rõ ràng là bạn sẽ không có quyền truy cập đến thuộc tính Outer.name ...! –

+0

@Jon là có bất kỳ tên kỹ thuật cụ thể cho thành ngữ này của việc tạo ra một trường hợp lớp bên trong không tĩnh? – Inquisitive

+0

@Inquisitive: Nó chỉ là một thể hiện của một lớp bên trong. –

9

Button.ClickEvent là một lớp bên trong không tĩnh nên một thể hiện của lớp này chỉ có thể tồn tại trong một thể hiện của Button.

Trong ví dụ mã thứ hai của bạn, bạn có một thể hiện của Button và bạn tạo một thể hiện của ClickEvent kèm theo trong trường hợp nút này ...

8

Lớp bên trong không tĩnh trong Java chứa tham chiếu bị ẩn trỏ tới một thể hiện của lớp bên ngoài được khai báo. Vì vậy, thông báo lỗi bạn nhận được ban đầu là cho bạn biết rằng bạn không thể tạo một thể hiện mới của bên trong lớp mà không cần chỉ định một thể hiện của lớp bên ngoài để nó được gắn vào.

Có lẽ lý do bạn chưa thấy cú pháp đó trước đó là các lớp bên trong thường được phân bổ trong một phương thức của lớp bên ngoài, nơi trình biên dịch tự động xử lý điều này.

3

Để tránh nhầm lẫn bản thân và những người lập trình với tính năng hiếm khi được sử dụng này, bạn luôn có thể làm cho các lớp bên trong tĩnh.

Trong trường hợp tham chiếu đến lớp ngoài là cần thiết, bạn có thể chuyển nó một cách rõ ràng trong hàm tạo.

+0

Hoặc làm cho các lớp bên trong riêng tư, và dính vào tiềm ẩn này. –

-1

Mã của bạn sẽ biên dịch, có bạn đã gõ

buttonClick(new Button().ClickEvent(button)); 

thay vì

buttonClick(new Button.ClickEvent(button));

như một constructor là một phương pháp và khi bạn gọi một phương thức trong Java bạn phải vượt qua danh sách các đối số, ngay cả khi nó trống.

+0

Thực ra, không, điều đó không hiệu quả, bởi vì nó cố gắng gọi phương thức ClickEvent (Button) với cái mà nó không tìm thấy ... –

+0

new Button(). ClickEvent mới (nút)? –

1

Bạn thực sự có thể làm điều đó, nhưng bạn phải khai báo ClickEvent như static bên Button, và sau đó bạn không nên có bất kỳ vấn đề sử dụng bạn sintax:

buttonClick(new Button.ClickEvent(button)); 

Về cơ bản static làm cho các lớp ClickEvent thuộc trực tiếp đến lớp Button thay vì một trường hợp cụ thể (ví dụ: new Button()) của Button.


Sau @ Jon Skeet dụ:

// Button.java 
class Button 
{ 

    public static class ClickEvent 
    { 
     public ClickEvent(Button b) 
     { 
      System.out.println("Instance: " + this.toString()); 
     } 
    } 
} 

// Test.java 
public class Test 
{ 
    public static void main(String[] args) 
    { 
     Button button = new Button(); 
     buttonClick(new Button.ClickEvent(button)); 
    } 

    public static void buttonClick (Button.ClickEvent ce) { 
    } 
} 
Các vấn đề liên quan