Javenue logo

Javenue

Программирование на Java

Информационные технологии

Анонимные классы в Java (anonymous inner classes)

English version of the article can be found here.

Практически во всех статьях об inner классах допущен ряд ошибок по поводу анонимных классов:

  • они не имеют имени;
  • они не могут быть объявлены статическими;
  • они могут быть созданы только один раз при декларировании.

Это все неправда!

Допустим у нас есть следующий код:

public class Anonymous {
   public static void main(String[] args) {
    Runnable anonym = new Runnable() {
      public void run() {
      }
    };
  }
}

Имя анонимного класса можно получить так:

anonym.getClass().toString().

Скорее всего вы получите что-то вроде этого: Anonymous$1.

Анонимный класс может быть как статическим, так и нестатическим. Это напрямую зависит от того, статическим или нестатическим является блок, в котором анонимный класс был объявлен. В примере, который указан выше, анонимный класс будет статическим. В этом случае можно создать еще один экземпляр класса таким способом:

Runnable anonym2 = (Runnable) anonym
    .getClass().newInstance().

В версии JDK 1.5 приведение типа (по-русски type cast :)) делать не нужно.

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

Вот небольшой пример. Для его упрощения, я не использовал алгоритм поиска необходимого конструктора и списка его формальных параметров, а также опустил обработку Exception.

public class Anonymous {
  public void nonStaticMethod() {
    Runnable anonym = new Runnable() {
      public void run() {
      }
    };
    Constructor[] constructors = anonym.getClass()
            .getDeclaredConstructors();
    Object[] params = new Object[1];
    params[0] = this;

    Runnable anonym2 = (Runnable) constructors[0].newInstance(params);
  }

  public static void main(String[] args) {
    Anonymous example = new Anonymous();
    example.nonStaticMethod();
  }
}

Интересующий нас конструктор имеет модификатор видимости protected. Функция getConstructors(), в отличии от getDeclaredConstructors(), вернет список только public конструкторов.

Надеюсь, статья была вам интересна. В любом случае, комментарии приветствуются.

Еще про внутренние классы в Java можно прочитать здесь - Внтуренние классы в Java.

Updated (26.08.2009): "За все, что вы когда-либо писали, вам придется отвечать всю оставшуюся жизнь..." (c) Костя Чапюк, то есть я

Время идет, многие статьи на блоге уже постарели и поседели... Тем не менее до сих пор находятся люди, которые не смотрят на дату публикации статей и почему-то хотят задеть меня или унизить мои "многолетназадшние" порывы вести свой блог и делиться с миром моими наблюдениями.

Например, комментарий от bsv:

пожалуйста получи имя этого анонимного класса .)
new Thread(new Runnable() {
    public void run() {
        …
    }
}).start();
твой пример по этому поводу, извени, слегка притянут за уши .)

Хотел сначала проигнорировать, но может кому-то интересно будет. Позволю себе немного упростить задание, а то с тремя точками плохо компилится...

public class LookHereItIs {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                while (true)
                    try { Thread.sleep(1000); }
                    catch (InterruptedException e) { }
            }
        }).start();

        ThreadGroup group = Thread.currentThread().getThreadGroup();
        Thread[] threads = new Thread[group.activeCount()];
        group.enumerate(threads);

        Field field;
        try { field = Thread.class.getDeclaredField("target"); }
        catch (NoSuchFieldException e) { throw new RuntimeException(e); }

        field.setAccessible(true);
        Runnable runnable;
        try { runnable = (Runnable) field.get(threads[1]); }
        catch (IllegalAccessException e) { throw new RuntimeException(e); }

        System.out.println(runnable.getClass().toString());
    }
}

P.S. Индекс "1" за уши не притянут... Можно было выкинуть из массива currentThread; оставшийся Thread - наш клиент.



Комментариев: 5

  Выйти

  * для публикации комментариев нужно  

bsv:

пожалуйста получи имя этого анонимного класса .)

new Thread(new Runnable() {
  public void run() {
    …
  }
}).start();

твой пример по этому поводу, извени, слегка притянут за уши .)

Anton Krupnov:

Очень хорошая статья, при внимательном чтении помогает понять механизм обработки анонимных классов, на мой взгляд это гораздо более глубокий уровень понимания.
Спасибо большое за такую информацию! :)

aaa:

статья хорошая. короткая. понятная. простая. без лишних загогулин. хорошо что есть люди которые такую инфу вывешивают. вообще классные наблюдения :)

Vitalii Lievashov:

Хилоу!! Время таки идет и уже много прошло с момента апдейта статьи, но все же напишу т.е спрошу: такие манипуляции с анонимными классами часто встречаются в повседневной рабочей деятельности программиста? жить без этого возможно ? :)

German Bakarev:

Ну если вы ничего не читали про рефлексию, то действительно, прям чудеса... На самом-то деле все правильно в документации указано. По вашему, модификаторы доступа - это тоже чушь и не правда, в рантайме мы сможем раскрутить любое поле и метод... Что же делать с инкапсуляцией? Тоже обман? Речь идет о безопасности разработчика/команды на стадии компиляции... дабы не использовали поля/методы, и не загромождали контекст текущего набора сущности...прайвед-низя трогать...очень надо? меняй модификатор... профайлинг без рефакторинга, тода рефлексия в помощь и вэлком в рантайме )))