Расширение интерфейса против создания с помощью анонимного класса

1

NOTE: Я знаю, что это опасно близко ко многим другим вопросам. Тем не менее, я не видел, что это не относится конкретно к интерфейсу Android OnClickListener. Я спрашиваю в общем смысле.

Я понимаю разницу между созданием интерфейса через анонимный класс... a la:

private final Runnable runnable = new Runnable() {
    @Override
    public void run() {
        draw();
    }

};

... и расширения интерфейса.

public class ClassyClass implements Runnable {
    ...
    //do other cool stuff here
    ...
    @Override
    public void run() {
        draw();
    }
    ...
    //and more here
    ...

}

Но, кроме очевидных преимуществ интерфейсов, таких как OnClickListener есть ли сильное преимущество для любого из этих вариантов?

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

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

Теги:
runnable
interface
anonymous-class

3 ответа

2
Лучший ответ

Ну вот пример, который показывает основные отличия. Это немного надуманное, но это просто для иллюстрации.

Вот анонимная версия:

class PrintBuilder {
    List<Runnable> printers = new LinkedList<>();

    List<Runnable> get() {
        return printers;
    }

    PrintBuilder add(final String line) {
        printers.add(new Runnable() {
            public void run() {
                System.out.println(line);
            }
        });

        return this;
    }
}

Вот вложенная версия:

class PrintBuilder {
    List<Printer> printers = new LinkedList<>();

    PrintBuilder add(String line) {
        printers.add(new Printer(line));
        return this;
    }

    List<Printer> get() {
        return printers;
    }

    static class Printer implements Runnable {
        String line;

        Printer(String line) {
            this.line = line;
        }

        public void run() {
            System.out.println(line);
        }
    }
}

Итак, вы можете видеть основные отличия:

  • Анонимный класс является внутренним и имеет неявную ссылку на прилагаемый экземпляр.

В частности, в приведенном выше примере PrintBuilder просачивается до тех пор, пока не будут уничтожены внутренние объекты Runnable. Во втором примере класс Runnable статичен, поэтому у него нет этой проблемы.

  • Анонимные классы немного короче.

Анонимному классу не нужны поля и конструкторы, потому что они неявные.

  • Большая разница в способе организации кода.

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

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

  • 0
    Огромное спасибо. Я ценю конкретные плюсы и минусы. Последний вопрос. Я вроде понял, и я видел, как другие говорили об этой утечке (в примере 1), но не могли бы вы объяснить, почему это происходит?
  • 1
    @BrassApparatus: это происходит потому, что по замыслу внутренние классы имеют неявную ссылку на свой включающий объект, чтобы иметь возможность доступа к своим полям и методам. Точно так же, как если бы у вас было поле PrintBuilder в принтере второго примера, и вы передали this как аргумент конструктора Printer.
Показать ещё 2 комментария
0

Оба подхода действительны. Какой из них зависит от вашего использования:

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

Первый фрагмент определяет класс без имени, который реализует интерфейс Runnable (и создает этот анонимный класс). Второй определяет класс с именем, который также реализует интерфейс Runnable.

Там нет различий в терминах наследования и реализации интерфейса, кроме того, что второй класс имеет имя, а первый - нет.

Второй фрагмент позволяет нескольким классам создавать экземпляр реализации Runnable, тогда как первый определяет класс, который известен только из его охватывающего класса. Иногда один лучше, иногда другой.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню