Подтвердить что ты не робот

Вызов метода анонимного класса

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

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

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

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

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

Спасибо

4b9b3361

Ответ 1

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

В основном это будет описано в разделе, посвященном Признаки вызова метода:

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

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

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

Здесь основное выражение представляет собой выражение выражение экземпляра экземпляра класса. Таким образом, для поиска используется анонимный тип.

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

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

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

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

Отражение также раскрывает этот метод. Созданный анонимный класс содержит этот метод, поэтому вы можете его восстановить и вызвать. Процесс тот же. Тот факт, что экземпляр из анонимного класса не имеет значения. Та же стратегия, что и в Как мне вызвать метод Java при указании имени метода как строки?.

Например,

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

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

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.

Ответ 3

Добавление в ответ Sotirios, здесь, как вызвать метод через отражение:

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

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

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

Ответ 4

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

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

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

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

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

Даже если мы не можем назвать тип, его не нужно называть, где его можно вывести.

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

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

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