Анонимные классы объявляются без указания в коде имени класса.
Анонимные классы могут быть созданы:
1) как реализация интерфейса
public class Foo {
// Анонимный класс, который реализует интерфейс SayHello
static SayHello h = new SayHello() {
@Override
public void say() {
System.out.println("Метод внутреннего анонимного класса");
}
};
public static void main(String[] args) {
h.say();
}
}
// somewhere
interface SayHello {
void say();
}
На практике, чаще, реализуют функциональные интерфейсы, с помощью анонимных классов, не смотря на наличие лямбд. Вот перечень функциональных интерфейсов из пакета java.util.function
. Это не магия, а просто набор готовых заготовок для ваших будущих реализаций.
2) как наследник определённого класса
public class External {
// Анонимный класс наследуется от класса Foo
static Foo foo = new Foo() {
@Override
public void show() {
super.show();
System.out.println("Метод внутреннего анонимного класса");
}
};
public static void main(String[] args) {
foo.show();
}
}
class Foo {
public void show() {
System.out.println("Метод суперкласса");
}
}
Анонимный класс может быть как статическим, так и нестатическим. Это напрямую зависит от того, статическим или нестатическим является блок, в котором анонимный класс был объявлен.
Если анонимный класс объявлен в статическом блоке:
import java.util.function.Consumer;
public class Anonymous {
public static void main(String[] args) {
Consumer<String> foo = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o + " TODO something useful!");
}
};
foo.accept("Работа анонимного класса в статическом методе.");
}
}
То его второй экземпляр можно создать так: Consumer otherInstance = foo.getClass().newInstance();
(эту строку нужно завернуть в try-catch
или пробросить исключения выше). И переиспользуем ту же логику, но уже в другом инстансе: otherInstance.accept("Работа второго экземпляра анонимного класса в статическом методе.");
.
Если же анонимный класс был объявлен внутри нестатического блока, то для создания второго экземпляра анонимного класса нужно передать в его конструктор ссылку на обрамляющий класс. В противном случае получим InstantiationException
.
Вот как это выглядит в коде:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Consumer;
public class Anonymous {
public void nonStaticMethod() {
// собственно наш анонимный класс
Consumer<String> foo = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o + " TODO something useful!");
}
};
foo.accept("Работа анонимного класса в НЕстатическом методе.");
// достаем конструктор у анонимного класса
Constructor[] constructors = foo.getClass().getDeclaredConstructors();
// Записываем ссылку на объект Anonymous в массив параметров, для конструктора.
Object[] params = new Object[1];
params[0] = this;
// Создаем новую ссылку типа Consumer
Consumer otherInstance = null;
try {
// создаем новый экземпляр анонимного класса, передав в его конструктор массив параметров, ссылку на текущий
// класс, в котором мы и осуществляем все манипуляции с анонимным классом.
otherInstance = (Consumer) constructors[0].newInstance(params);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
otherInstance.accept("Работа второго экземпляра анонимного класса в НЕстатическом методе.");
}
public static void main(String[] args) {
Anonymous anon = new Anonymous();
anon.nonStaticMethod();
}
}
Любой анонимный внутренний класс может за один раз реализовать только один интерфейс. Так же, за один раз можно либо расширить класс, либо реализовать интерфейс, но не одновременно.
Анонимные классы полезны в некоторых "узких" участках кода, когда нет необходимости их потом переиспользовать где-то еще. Чаще всего на практике используют лишь один экземпляр анонимного класса. Если же реализацию интерфейса придется использовать много раз в коде — то лучше использовать лямбда-выражения (лямбды), которые стали доступны начиная с JAVA 8
.
Например, реализацию интерфейса Consumer
, в текущем уроке, можно переписать с помощью лямбд:
import java.util.function.Consumer;
public class Anonymous {
public static void main(String[] args) {
Consumer<String> foo = o -> System.out.println(o + " TODO something useful!");
foo.accept(" Using lambda :)");
}
}
Преимущества и недостатки между анонимными классами и лямбдами постепенно меняются в сторону лямбд. Об этих различиях можно почитать в статье на Хабре.
Отдельно стоит упомянуть, что не все версии Android поддерживают лямбды. И многим будет проще использовать анонимные классы, чем прикручивать лямбды.
Вам ответят команда поддержки Хекслета или другие студенты.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
Зарегистрируйтесь или войдите в свой аккаунт