2015-10-05 3 views
36

я узнал на днях, что вы можете сделать этоВызов метода анонимного класса

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

Это кажется действительно странным для меня. Разумеется, статический тип создаваемого объекта Object, поэтому нет метода hello()? Разве это не совсем бессмысленно (например, невозможно вызвать hello).

У меня есть 2 вопроса об этом.

  1. Может кто-нибудь указать мне на часть спецификации, которая обращается к этому вопросу?
  2. Я правильно понимаю, что единственный способ, которым вы можете ссылаться hello, это вот так. Как насчет отражения?

Благодаря

+0

[JLS-15.9.5. Анонимные объявления классов] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.9.5) –

+0

Вы должны быть в состоянии добраться до него через отражение. Но да, это почти совершенно бессмысленно.Если вы хотите вызвать методы, вы обычно реализуете интерфейс в своем анонимном классе. – Thilo

+4

Ну, если вы создаете анонимный класс более определенного типа (например, анонимную реализацию * интерфейса *), вы можете удерживать ссылку в переменной, которая позволит вам называть эти методы по своему усмотрению. –

ответ

16

Может ли кто-нибудь указать мне на часть спецификации, которая обращается к этому вопросу?

Это будет главным образом определено в секции относительно Method invocation expressions:

Первым шагом в обработке вызова метода во время компиляции, чтобы выяснить, имя метода, который будет вызван, и какой класс или интерфейс для поиска определений методов этого имени.

Для класса или интерфейса для поиска, есть шесть случаев, чтобы рассмотреть, в зависимости от формы, которая предшествует левую скобку в MethodInvocation:

  • [...]
  • Если форма Primary . [TypeArguments] Identifier, то пусть T будет тип первичного выражения. Класс или интерфейс для поиска : T, если T - тип класса или интерфейса или верхняя граница T, если T - это переменная типа.

Здесь Первичное выражение является class instance creation expression. Таким образом, для поиска используется анонимный тип.

Я правильно понял, что единственный способ, которым вы можете позвонить, - это сразу вот так. Как насчет отражения?

До тех пор пока выражение анонимного типа T, будь то посредством прямого доступа, как у вас есть, или через дженериков, у вас есть доступ (регулярные правила доступа применяются) к членам, которые T заявляет. Это не ограничивается методами. Вы можете получить доступ к полям или типам, хотя это не так полезно для типов. Например,

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

Поскольку нет никакого способа, чтобы обратиться к вложенному типу без типа ограждающей, вы не можете объявить переменную этого вложенного типа. Полезность снижается очень быстро. (Предположительно, поэтому у вас не может быть вложенного типа static в этом анонимном классе.)

Отражение также раскрывает этот метод. Созданный анонимный класс содержит этот метод, поэтому вы можете его восстановить и вызвать. Процесс тот же. Тот факт, что экземпляр из анонимного класса не имеет значения. Используется та же стратегия, что и в приложении How do I invoke a Java method when given the method name as a string?.

Например,

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

Никогда не писать код, как это.

+2

@ ElliottFrisch Этого не должно быть. 'new Object() {} .getClass()' возвращает 'класс com.example.Example $ 1' для меня. –

+0

Oracle Java 8, Linux. 'o.getClass(). getMethods()' не дал метод 'hello'. Вот где я остановился. –

+4

@elliot Если вы использовали свой код, это не сработает, потому что метод не является общедоступным. –

12

Как писал, не существует способ получить анонимные методы из экземпляра Object. И это делает Anonymous classes бесполезным. Но вы могли бы (и я обычно) использовать его для реализации интерфейса. Нечто подобное,

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

Или, чаще всего, для вызова спинок с JFC/Swing и ActionListener.

11

Добавление к Sotirios' answer, вот как для вызова метода с помощью отражения:

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

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

Это позволяет вызвать метод несколько раз, но кроме этого, имеет мало смысла.

+0

Спасибо. Я согласен, что это необходимо для полного ответа. –

9

Анонимный класс в пользу ленивых программистов - называя вещи слишком сложно :)

Анонимный класс очень похож на местного класса. Если локальный класс предназначен только для создания одного объекта, который затем используется только как супертип, мы можем вместо этого создать анонимный класс, который является более кратким.

Анонимный класс является неоспоримым (для программиста), что прекрасно, потому что нам не нужно ссылаться на него снова. Однако для компилятора класс очень назван, и нет причин рассматривать его иначе, чем явно названные классы. Статический тип выражения является конкретным подклассом, а члены объекта являются членами этого класса. Эта функция (возможность позвонить hello()) бессмысленно? Только если вы считаете, что местный класс бессмыслен (и действительно, локальный класс редко используется). Вот example, где я использовал эту функцию (для удовольствия).

Несмотря на то, что тип неоспоримый, тип может выжить как сам по API. Например,

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

Несмотря на то, что мы не можем назвать тип, он не должен быть назван, где можно сделать вывод.

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

И мы можем создать Puzzlers для людей, которые не ожидали, статического типа

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

Итак, мое предположение совершенно неверно - вы можете дважды называть 'hello()' без отражения. Это круто, но и немного смешно. Вдохновленный вашим ответом, мне это удалось с помощью 'Collections.nCopies (2, new Object() {...}). ForEach (o -> o.hello());' –

+2

да, ваша лучшая демонстрация. – ZhongYu

+1

Хотя программист не может назвать конкретный тип выражения, java также предоставляет множество мест, где вам не нужно указывать тип, например. лямбда-параметра. – ZhongYu

Смежные вопросы