2015-10-05 18 views
36

Tôi đã học được ngày khác mà bạn có thể làm điều nàyGọi một phương pháp của một lớp vô danh

new Object() { 
    void hello() { 
     System.out.println("Hello World!"); 
    } 
}.hello(); 

Điều này có vẻ thực sự kỳ lạ đối với tôi. Chắc chắn loại tĩnh của đối tượng được tạo là Object, vì vậy không có phương thức hello()? Không phải là nó gần như hoàn toàn vô nghĩa (không thể gọi ví dụ hello hai lần).

Tôi có 2 câu hỏi về điều này.

  1. Ai đó có thể chỉ cho tôi phần đặc tả giải quyết vấn đề này không?
  2. Tôi có nghĩ rằng cách duy nhất bạn có thể gọi hello là ngay lập tức như thế này. Điều gì về sự phản ánh?

Cảm ơn

+0

[JLS-15.9.5. Tuyên bố lớp ẩn danh] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.9.5) –

+0

Bạn sẽ có thể truy cập thông qua phản ánh. Nhưng có, điều này gần như hoàn toàn vô nghĩa.Nếu bạn muốn gọi các phương thức, bạn thường thực hiện một Giao diện trong lớp ẩn danh của bạn. – Thilo

+4

Vâng, nếu bạn tạo một lớp ẩn danh của một loại cụ thể hơn (như, việc triển khai ẩn danh * giao diện *), bạn có thể giữ tham chiếu trong biến để cho phép bạn gọi những phương thức đó theo ý muốn. –

Trả lời

16

Ai đó có thể chỉ cho tôi phần đặc tả giải quyết vấn đề này không?

này chủ yếu sẽ được xác định trong phần liên quan đến Method invocation expressions:

Bước đầu tiên trong việc xử lý một invocation phương pháp tại thời gian biên dịch là con số ra tên của phương pháp này được gọi và đó lớp hoặc giao diện để tìm kiếm các định nghĩa về các phương thức của tên đó.

Đối với lớp hoặc giao diện để tìm kiếm, có sáu trường hợp để xem xét, tùy thuộc vào hình thức mà trước ngoặc trái của MethodInvocation:

  • [...]
  • Nếu biểu mẫu là Primary . [TypeArguments] Identifier, sau đó để T là loại biểu thức Chính. Lớp hoặc giao diện để tìm kiếm là T nếu T là loại hoặc loại giao diện hoặc giới hạn trên của T nếu T là biến kiểu.

Ở đây, biểu tiểuclass instance creation expression. Vì vậy, loại tìm kiếm là loại ẩn danh.

Tôi có nghĩ rằng cách duy nhất bạn có thể gọi xin chào là ngay lập tức như thế này. Điều gì về sự phản ánh?

Chừng nào một biểu thức đánh giá vào loại vô danh T, dù là thông qua truy cập trực tiếp như bạn có, hoặc thông qua Generics, bạn có thể truy cập (access rules thường xuyên áp dụng) cho các thành viên rằng T tuyên bố. Điều này không giới hạn trong các phương thức. Bạn có thể truy cập các trường hoặc loại, mặc dù nó không hữu ích cho các loại. Ví dụ,

Object var = new Object() { 
    class Nested { 
    } 
}.new Nested(); 

Kể từ khi không có cách nào để tham khảo các loại lồng nhau mà không loại kèm theo, bạn có thể không khai báo một biến kiểu lồng nhau. Tính hữu dụng giảm rất nhanh. (Có lẽ, đó cũng là lý do tại sao bạn không thể có một loại lồng nhau static trong lớp ẩn danh này.)

Phản ánh cũng cho thấy phương pháp này. Lớp ẩn danh được tạo ra chứa phương thức này, vì vậy bạn có thể lấy nó và gọi nó. Quá trình này như nhau. Thực tế là trường hợp là từ một lớp vô danh không quan trọng. Chiến lược tương tự như được trình bày trong How do I invoke a Java method when given the method name as a string? được áp dụng.

Ví dụ,

Object ref = new Object() { 
    public void method() { 
     System.out.println("hidden"); 
    } 
}; 
Class<?> anonymousClass = ref.getClass(); 
Method method = anonymousClass.getMethod("method"); 
method.invoke(ref, new Object[0]); 

Đừng bao giờ viết mã như thế này.

+2

@ElliottFrisch Đó không phải là trường hợp. 'new Object() {} .getClass()' trả về 'class com.example.Example $ 1' cho tôi. –

+0

Oracle Java 8, Linux. 'o.getClass(). getMethods()' không tạo ra phương thức 'hello'. Đó là nơi tôi dừng lại. –

+4

@elliot Nếu bạn sử dụng mã của họ, điều đó sẽ không hoạt động vì phương thức không công khai. –

12

Như được đăng, không có một cách để có được những phương pháp vô danh từ Object dụ. Và, nó làm cho Anonymous classes trông vô nghĩa. Nhưng, bạn có thể (và tôi thường sẽ) sử dụng nó để thực hiện một giao diện. Một cái gì đó như,

static interface Hello { 
    void hello(); 
} 
public static void main(String[] args) { 
    Hello o = new Hello() { 
     public void hello() { 
      System.out.println("Hello World!"); 
     } 
    }; 
    o.hello(); 
} 

Hoặc, thông thường, để gọi lại với JFC/Swing và ActionListener.

11

Thêm vào Sotirios' answer, đây là cách để gọi phương pháp này thông qua phản ánh:

Object o = new Object() { 
    void hello() { 
     System.out.println("Hello World!"); 
    } 
}; 

Method hello = o.getClass().getDeclaredMethod("hello"); 
hello.invoke(o); 

này cho phép bạn gọi phương thức nhiều hơn một lần, nhưng khác hơn thế, không khôn ngoan.

+0

Cảm ơn. Tôi đồng ý rằng cần thiết cho một câu trả lời hoàn chỉnh. –

9

Lớp ẩn danh là vì lợi ích của người lập trình lười biếng - việc đặt tên quá khó :)

Lớp ẩn danh rất giống lớp địa phương. Nếu một lớp địa phương chỉ là để tạo một đối tượng, mà sau đó chỉ được sử dụng như một siêu kiểu, chúng ta có thể tạo một lớp ẩn danh thay vì gọn gàng hơn.

Lớp ẩn danh là không thể phát hiện (đối với lập trình viên), điều này là tốt vì chúng tôi không cần tham chiếu lại lần nữa. Tuy nhiên, đối với trình biên dịch, lớp được đặt tên rất nhiều và không có lý do nào để xử lý nó khác với các lớp được đặt tên rõ ràng. Kiểu tĩnh của biểu thức là lớp con cụ thể và các thành viên của đối tượng là các thành viên của lớp đó. Tính năng này (có thể gọi hello()) vô nghĩa? Chỉ khi bạn xem xét lớp địa phương vô nghĩa (và thực sự là lớp địa phương hiếm khi được sử dụng). Đây là một số example nơi tôi đã sử dụng tính năng đó (để giải trí).

Mặc dù loại không thể thể hiện, loại có thể tồn tại như chính nó thông qua API. Ví dụ:

Objects.requireNonNull(new Object(){ void hello(){} }).hello(); 

Mặc dù chúng tôi không thể đặt tên loại, nó không cần phải được đặt tên khi nó được suy ra.

Collections.singleton(new Object(){ void hello(){} }) 
     .forEach(o->{ o.hello(); o.hello(); }); 

Và chúng ta có thể tạo puzzlers cho những người không mong đợi kiểu tĩnh

Collections.singleton(new Object(){}).add(new Object()); // does not compile! why? 
+1

Vì vậy, tiền đề của tôi là hoàn toàn sai - bạn có thể gọi 'hello()' hai lần mà không có sự phản ánh. Điều này là mát mẻ, nhưng cũng có một chút vô lý. Lấy cảm hứng từ câu trả lời của bạn, tôi quản lý nó với 'Collections.nCopies (2, new Object() {...}) forEach (o -> o.hello());' –

+2

yeah, bạn là một trình diễn tốt hơn. – ZhongYu

+1

Mặc dù lập trình viên không thể đặt tên cho loại biểu thức cụ thể, java cũng cung cấp nhiều địa điểm mà bạn không cần đặt tên cho loại, ví dụ: tham số lambda. – ZhongYu

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