Какая польза от анонимных классов в Java? Можно ли сказать, что использование анонимного класса является одним из преимуществ Java?
Какая польза от анонимных классов в Java? Можно ли сказать, что использование анонимного класса является одним из преимуществ Java?
Ответы:
Под «анонимным классом» я понимаю, что вы имеете в виду анонимный внутренний класс .
Анонимный внутренний класс может пригодиться при создании экземпляра объекта с определенными «дополнениями», такими как переопределяющие методы, без необходимости фактически создавать подкласс класса.
Я склонен использовать его как ярлык для подключения прослушивателя событий:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
Использование этого метода делает кодирование немного быстрее, поскольку мне не нужно создавать дополнительный класс, который реализует ActionListener
- я могу просто создать экземпляр анонимного внутреннего класса, фактически не создавая отдельный класс.
Я использую эту технику только для «быстрых и грязных» задач, когда создание всего класса кажется ненужным. Наличие нескольких анонимных внутренних классов, которые делают одно и то же, должно быть реорганизовано в реальный класс, будь то внутренний класс или отдельный класс.
overloading methods
а нет overriding methods
?
Анонимные внутренние классы фактически являются замыканиями, поэтому их можно использовать для эмуляции лямбда-выражений или «делегатов». Например, возьмем этот интерфейс:
public interface F<A, B> {
B f(A a);
}
Вы можете использовать это анонимно для создания первоклассной функции в Java. Допустим, у вас есть следующий метод, который возвращает первое число, большее, чем i в данном списке, или i, если число не больше:
public static int larger(final List<Integer> ns, final int i) {
for (Integer n : ns)
if (n > i)
return n;
return i;
}
И затем у вас есть другой метод, который возвращает первое число, меньшее, чем i в данном списке, или i, если ни одно число не меньше:
public static int smaller(final List<Integer> ns, final int i) {
for (Integer n : ns)
if (n < i)
return n;
return i;
}
Эти методы практически идентичны. Используя первоклассный тип функции F, мы можем переписать их в один метод следующим образом:
public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
for (T t : ts)
if (f.f(t))
return t;
return z;
}
Вы можете использовать анонимный класс для использования метода firstMatch:
F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
Boolean f(final Integer n) {
return n > 10;
}
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);
Это действительно надуманный пример, но легко увидеть, что возможность передавать функции, как если бы они были значениями, является довольно полезной функцией. Смотрите "Может ли ваш язык программирования сделать это" от самого Джоэла.
Хорошая библиотека для программирования Java в этом стиле: Функциональная Java.
Анонимный внутренний класс используется в следующем сценарии:
1.) Для переопределения (подкласса), когда определение класса невозможно использовать, кроме текущего случая:
class A{
public void methodA() {
System.out.println("methodA");
}
}
class B{
A a = new A() {
public void methodA() {
System.out.println("anonymous methodA");
}
};
}
2.) Для реализации интерфейса, когда реализация интерфейса требуется только для текущего случая:
interface interfaceA{
public void methodA();
}
class B{
interfaceA a = new interfaceA() {
public void methodA() {
System.out.println("anonymous methodA implementer");
}
};
}
3.) Аргумент определен анонимным внутренним классом:
interface Foo {
void methodFoo();
}
class B{
void do(Foo f) { }
}
class A{
void methodA() {
B b = new B();
b.do(new Foo() {
public void methodFoo() {
System.out.println("methodFoo");
}
});
}
}
Я использую их иногда как синтаксический взлом для создания экземпляров Map:
Map map = new HashMap() {{
put("key", "value");
}};
против
Map map = new HashMap();
map.put("key", "value");
Это экономит некоторую избыточность при выполнении множества операторов put. Тем не менее, я также столкнулся с проблемами при этом, когда внешний класс должен быть сериализован посредством удаленного взаимодействия.
Они обычно используются как многословная форма обратного вызова.
Я полагаю, вы могли бы сказать, что они являются преимуществом по сравнению с тем, что их нет, и нужно каждый раз создавать именованный класс, но подобные концепции гораздо лучше реализованы в других языках (в виде замыканий или блоков).
Вот пример свинга
myButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do stuff here...
}
});
Хотя это все еще беспорядочно многословно, это намного лучше, чем заставлять вас определять именованный класс для каждого отбрасывающего слушателя, как это (хотя в зависимости от ситуации и повторного использования, это все же может быть лучшим подходом)
myButton.addActionListener(e -> { /* do stuff here */})
или myButton.addActionListener(stuff)
будет более кратким.
Вы используете его в ситуациях, когда вам нужно создать класс для определенной цели внутри другой функции, например, в качестве слушателя, в качестве исполняемого объекта (для создания потока) и т. Д.
Идея состоит в том, что вы вызываете их из кода функции, чтобы вы никогда не обращались к ним в другом месте, поэтому вам не нужно называть их. Компилятор просто перечисляет их.
Они по сути являются синтаксическим сахаром, и, как правило, их следует перемещать в другое место по мере их роста.
Я не уверен, является ли это одним из преимуществ Java, хотя, если вы используете их (и, к сожалению, мы все часто их используем), вы могли бы утверждать, что они едины.
GuideLines для анонимного класса.
Анонимный класс объявляется и инициализируется одновременно.
Анонимный класс должен расширяться или реализовываться до одного и только одного класса или интерфейса, соответственно.
Так как у класса anonymouse нет имени, его можно использовать только один раз.
например:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
ref.getClass().newInstance()
.
Да, анонимные внутренние классы - это одно из преимуществ Java.
С анонимным внутренним классом у вас есть доступ к final и переменным-членам окружающего класса, и это удобно для слушателей и т. Д.
Но главное преимущество заключается в том, что код внутреннего класса, который (по крайней мере, должен быть) тесно связан с окружающим классом / методом / блоком, имеет определенный контекст (окружающий класс, метод и блок).
new Thread() {
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
System.out.println("Exception message: " + e.getMessage());
System.out.println("Exception cause: " + e.getCause());
}
}
}.start();
Это также один из примеров для анонимного внутреннего типа, использующего поток
я использую анонимные объекты для вызова новых потоков ..
new Thread(new Runnable() {
public void run() {
// you code
}
}).start();
Внутренний класс связан с экземпляром внешнего класса и есть два особых вида: Местный класс и анонимный класс . Анонимный класс позволяет нам одновременно объявлять и создавать экземпляр класса, что делает код кратким. Мы используем их, когда нам нужен локальный класс только один раз, поскольку у них нет имени.
Рассмотрим пример из документа, где у нас есть Person
класс:
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
}
public void printPerson() {
// ...
}
}
и у нас есть метод для печати членов, которые соответствуют критериям поиска:
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
где CheckPerson
интерфейс как:
interface CheckPerson {
boolean test(Person p);
}
Теперь мы можем использовать анонимный класс, который реализует этот интерфейс, чтобы указать критерии поиска как:
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
Здесь интерфейс очень прост, а синтаксис анонимного класса кажется громоздким и неясным.
Java 8 ввел термин « функциональный интерфейс», который представляет собой интерфейс только с одним абстрактным методом, поэтому мы можем сказать, CheckPerson
что это функциональный интерфейс. Мы можем использовать лямбда-выражение, которое позволяет нам передавать функцию в качестве аргумента метода в виде:
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
Predicate
Вместо интерфейса мы можем использовать стандартный функциональный интерфейс CheckPerson
, что еще больше сократит объем требуемого кода.
Анонимный внутренний класс может быть полезен, предоставляя разные реализации для разных объектов. Но следует использовать очень экономно, так как это создает проблемы для читаемости программы.
Одно из основных применений анонимных классов в финализации классов, которое называется finalizer guardian . В мире Java следует избегать использования методов finalize до тех пор, пока они вам действительно не понадобятся. Вы должны помнить, что когда вы переопределяете метод finalize для подклассов, вы всегда должны вызывать его super.finalize()
, потому что метод finalize суперкласса не будет вызываться автоматически, и у вас могут возникнуть проблемы с утечками памяти.
поэтому, учитывая вышеупомянутый факт, вы можете просто использовать анонимные классы, такие как:
public class HeavyClass{
private final Object finalizerGuardian = new Object() {
@Override
protected void finalize() throws Throwable{
//Finalize outer HeavyClass object
}
};
}
Используя эту технику, вы освободили себя и других разработчиков от необходимости обращаться super.finalize()
к каждому подклассу HeavyClass
метода финализации.
Кажется, здесь никто не упоминал, но вы также можете использовать анонимный класс для хранения аргумента универсального типа (который обычно теряется из-за стирания типа) :
public abstract class TypeHolder<T> {
private final Type type;
public TypeReference() {
// you may do do additional sanity checks here
final Type superClass = getClass().getGenericSuperclass();
this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public final Type getType() {
return this.type;
}
}
Если вы создадите экземпляр этого класса анонимным способом
TypeHolder<List<String>, Map<Ineger, Long>> holder =
new TypeHolder<List<String>, Map<Ineger, Long>>() {};
тогда такой holder
экземпляр будет содержать не стираемое определение переданного типа.
Это очень удобно для построения валидаторов / десериализаторов. Также вы можете создать экземпляр обобщенного типа с отражением (поэтому, если вы когда-нибудь хотели сделать это new T()
в параметризованном типе - милости просим!) .
Лучший способ оптимизировать код. Также мы можем использовать переопределяющий метод класса или интерфейса.
import java.util.Scanner;
abstract class AnonymousInner {
abstract void sum();
}
class AnonymousInnerMain {
public static void main(String []k){
Scanner sn = new Scanner(System.in);
System.out.println("Enter two vlaues");
int a= Integer.parseInt(sn.nextLine());
int b= Integer.parseInt(sn.nextLine());
AnonymousInner ac = new AnonymousInner(){
void sum(){
int c= a+b;
System.out.println("Sum of two number is: "+c);
}
};
ac.sum();
}
}
Анонимный внутренний класс используется для создания объекта , который никогда не будет ссылаться снова. Он не имеет имени и объявляется и создается в том же заявлении. Это используется там, где вы обычно используете переменную объекта. Вы заменяете переменную new
ключевым словом, вызовом конструктора и определением класса внутри {
и }
.
При написании многопоточной программы на Java она обычно выглядит так
ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();
ThreadClass
Используется здесь будет определена пользователем. Этот класс будет реализовывать Runnable
интерфейс, необходимый для создания потоков. В метод (единственный метод в ) должна быть реализована , а также. Понятно, что избавление от этого было бы более эффективным, и именно поэтому существуют анонимные внутренние классы.ThreadClass
run()
Runnable
ThreadClass
Посмотрите на следующий код
Thread runner = new Thread(new Runnable() {
public void run() {
//Thread does it's work here
}
});
runner.start();
Этот код заменяет ссылку на task
самый верхний пример. Вместо того, чтобы иметь отдельный класс, Anonymous Inner Class внутри Thread()
конструктора возвращает безымянный объект, который реализует Runnable
интерфейс и переопределяет run()
метод. Метод run()
будет включать в себя операторы, которые выполняют работу, требуемую потоком.
Отвечая на вопрос о том, является ли анонимные внутренние классы одним из преимуществ Java, я должен сказать, что я не совсем уверен, поскольку в настоящее время я не знаком со многими языками программирования. Но я могу сказать, что это определенно более быстрый и простой метод кодирования.
Ссылки: Самс научит себя Java за 21 день, седьмое издание
Еще одно преимущество:
поскольку вы знаете, что Java не поддерживает множественное наследование, поэтому, если вы используете своего рода класс «Thread» в качестве анонимного класса, у класса все еще остается один пробел для расширения любого другого класса.