Как анонимные внутренние классы используются в Java?

Что такое использование анонимных классов в Java? Можем ли мы сказать, что использование анонимного класса является одним из преимуществ Java?

Ответ 1

Под "анонимным классом" я понимаю, что вы имеете в виду анонимный внутренний класс.

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

Я склонен использовать его как ярлык для подключения прослушивателя событий:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

Использование этого метода делает кодирование немного быстрее, поскольку мне не нужно создавать дополнительный класс, который реализует ActionListener - я могу просто создать экземпляр анонимного внутреннего класса, фактически не создавая отдельный класс.

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

Ответ 2

Анонимные внутренние классы эффективно закрываются, поэтому их можно использовать для эмуляции лямбда-выражений или "делегатов". Например, возьмите этот интерфейс:

public interface F<A, B> {
   B f(A a);
}

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

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return 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.

Ответ 3

Анонимный внутренний класс используется в следующем сценарии:

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

Ответ 4

Я иногда использую их как синтаксический хак для создания карты:

Map map = new HashMap() {{
   put("key", "value");
}};

против

Map map = new HashMap();
map.put("key", "value");

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

Ответ 5

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

Я полагаю, вы могли бы сказать, что они являются преимуществом по сравнению с отсутствием их и необходимостью создавать именованный класс каждый раз, но аналогичные концепции реализованы гораздо лучше на других языках (как закрытие или блоки)

Здесь пример качания

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

Хотя это все еще бесполезно многословно, это намного лучше, чем заставлять вас определять именованный класс для каждого раздающего прослушивателя, подобного этому (хотя в зависимости от ситуации и повторного использования это может быть лучшим подходом)

Ответ 6

Вы используете его в ситуациях, когда вам нужно создать класс для определенной цели внутри другой функции, например, в качестве слушателя, как runnable (для создания потока) и т.д.

Идея состоит в том, что вы вызываете их из кода функции, чтобы вы никогда не ссылались на них в другом месте, поэтому вам не нужно их называть. Компилятор просто перечисляет их.

Они по сути являются синтаксическим сахаром и обычно должны перемещаться в другом месте по мере их роста.

Я не уверен, что это одно из преимуществ Java, хотя, если вы их используете (и мы все часто используем их, к сожалению), тогда вы можете утверждать, что они один.

Ответ 7

Справочные листы для анонимного класса.

  • Анонимный класс объявляется и инициализируется одновременно.

  • Анонимный класс должен расширять или реализовывать только один и только один класс или интерфейс.

  • Поскольку класс anonymouse не имеет имени, его можно использовать только один раз.

например:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

Ответ 8

Да, анонимные внутренние классы определенно являются одним из преимуществ Java.

С анонимным внутренним классом у вас есть доступ к конечным и переменным-членам окружающего класса, и это пригодится слушателям и т.д.

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

Ответ 9

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

Это также один из примеров анонимного внутреннего типа с использованием потока

Ответ 10

Я использую анонимные объекты для вызова новых потоков.

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

Ответ 11

внутренний класс связан с экземпляром внешнего класса и есть два специальных вида: Local class и Анонимный класс. Анонимный класс позволяет нам объявлять и создавать экземпляр класса одновременно, поэтому делает код кратким. Мы используем их, когда нам нужен локальный класс только один раз, поскольку у них нет имени.

Рассмотрим пример из doc, где у нас есть класс 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 является функциональным интерфейсом. Мы можем использовать Lambda Expression, который позволяет нам передать функцию как аргумент метода как:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

Вместо интерфейса CheckPerson мы можем использовать стандартный функциональный интерфейс Predicate, который дополнительно уменьшит требуемый код.

Ответ 12

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

Ответ 13

Одно из основных применений анонимных классов в завершении класса, которое называется опекуном finalizer. В 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, который нуждается в методе окончательной доработки.

Ответ 14

Вы можете использовать анонимный класс таким образом

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

Ответ 15

Кажется, что здесь никто не упоминается, но вы также можете использовать анонимный класс для хранения аргумента generic type (который обычно теряется из-за стирания типа):

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() в параметризованном типе - приветствуются!).

Недостатки/Ограничения

  • Вы должны явно передать общий параметр. В противном случае это приведет к потере параметра
  • Каждый экземпляр будет стоить вам дополнительного класса, который должен быть сгенерирован компилятором, что приводит к загрязнению кластерного пути /jar bloating

Ответ 16

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

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

}

Ответ 17

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

При написании Threaded Program в 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 work here
    }
});
runner.start();

Этот код заменяет ссылку на task в самом верхнем примере. Вместо того, чтобы иметь отдельный класс, Анонимный Внутренний класс внутри конструктора Thread() возвращает неназванный объект, который реализует интерфейс Runnable и переопределяет метод run(). Метод run() будет включать в себя операторы внутри, которые выполняют работу, требуемую потоком.

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

Ссылки: Sams Teach Yourself Java в 21 день Седьмое издание

Ответ 18

Еще одно преимущество:
Как вы знаете, Java не поддерживает множественное наследование, поэтому, если вы используете класс "Thread" kinda как анонимный класс, тогда класс по-прежнему имеет одно пространство, оставшееся для расширения любого другого класса.