Подтвердить что ты не робот

Почему интерфейсы предпочитают абстрактные классы?

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

Я попытался дать несколько ответов вроде:

  • Мы можем получить только одну расширенную функциональность
  • они являются 100% абстрактными
  • Реализация не жестко закодирована

Они попросили меня взять любой из JDBC-апи, который вы используете. "Почему они являются интерфейсами?".

Могу ли я получить лучший ответ для этого?

4b9b3361

Ответ 1

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

  • Дайте им ответ, который они хотят.
  • С уважением не согласен.

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

Однако, если вы создадите убедительный (и правильный) аргумент в своем несогласии, тогда интервьюер может принять к сведению. Во-первых, выделите положительные моменты в интерфейсах, это ДОЛЖНО. Во-вторых, я бы сказал, что интерфейсы лучше во многих сценариях, но они также приводят к дублированию кода, что отрицательно. Если у вас есть широкий массив подклассов, которые будут выполнять в основном одну и ту же реализацию, плюс дополнительные функции, тогда вам может понадобиться абстрактный класс. Это позволяет вам иметь много похожих объектов с мелкозернистыми деталями, тогда как с только интерфейсами у вас должно быть много разных объектов с почти дублирующимся кодом.

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

Ответ 2

В общем, и это отнюдь не "правило", которое следует слепо следовать, наиболее гибкая схема:

interface
   abstract class
       concrete class 1       
       concrete class 2

Интерфейс существует по нескольким причинам:

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

Это означает, что вы можете использовать уже существующие классы (или просто классы, которые ДОЛЖНЫ расшириться от чего-то еще) и заставить их работать с вашим кодом.

Абстрактный класс содержит все общие биты для конкретных классов. Абстрактный класс расширен с того момента, когда вы пишете новые классы или изменяете классы, которые вы хотите расширить (предполагая, что они распространяются из java.lang.Object).

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

Ответ 3

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

Ответ 4

Вы можете реализовать несколько интерфейсов, но вы можете наследовать только один класс

Ответ 5

Абстрактные классы

1. Невозможно создать экземпляр независимо от их производных классов. Абстрактные конструкторы классов называются только их производными классами.

2. Определить абстрактные сигнатуры участников, которые должны реализовывать базовые классы.

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

4. Включите данные, хранящиеся в полях.

5.Allow для (виртуальных) членов, которые имеют реализацию и, следовательно, обеспечивают реализацию элемента по умолчанию для класса-получателя.

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

Интерфейс

1. Невозможно создать экземпляр.

2. Выполнение всех элементов интерфейса происходит в базовом классе. Невозможно реализовать только некоторые члены в классе реализации.

3. Расширяющиеся интерфейсы с дополнительными элементами нарушают совместимость версий.

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

5. Все члены автоматически виртуальны и не могут включать какую-либо реализацию.

6. Хотя реализация по умолчанию не может появляться, классы, реализующие интерфейсы, могут продолжать выводить друг из друга.

Ответ 6

Как devinb и другие упоминают, похоже, что интервьюер показывает свое невежество в том, что вы не принимаете своих действительных ответов.

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

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

Ответ 7

Абстрактные классы имеют ряд потенциальных ловушек. Например, если вы переопределите метод, метод super() не вызывается, если вы его явно не вызвали. Это может вызвать проблемы для плохо реализованных переопределяющих классов. Кроме того, существуют потенциальные проблемы с equals() при использовании наследования.

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

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

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

"Почему интерфейсы предпочтительнее Абстрактные классы?"

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

Но, глядя на вопрос интервью, лучший вопрос действительно "Когда интерфейсы должны быть предпочтительнее абстрактных классов?" (и наоборот).

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

Ответ 11

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


Во-первых, ответ "только один суперкласс" хромает. Любой, кто дал мне этот ответ в интервью, быстро встретился с "С++ существовал до того, как Java и С++ имели несколько суперклассов. Почему вы думаете, что Джеймс Гослинг разрешил только один суперкласс для Java?"

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


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

Пример:

public void foo(Hashtable bar);

против.

public void foo(Map bar);

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


В-третьих, интерфейсы позволяют публичным методам в конкретных реализациях класса быть "private". Если метод не объявлен в интерфейсе, то метод не может использоваться (или неправильно используется) классами, которые не имеют бизнеса, используя этот метод. Это подводит меня к пункту 4....


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

public interface FragmentVisitor {
    public void visit(Node node);
}

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


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

Да, конечно, некоторые такие методы могут быть защищены. Однако печально защищенные методы также видны другим классам в одном пакете. И если метод абстрактного класса реализует интерфейс, метод должен быть общедоступным.

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


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

Ответ 12

Если они думают, что X лучше, чем Y, я бы не стал беспокоиться о том, чтобы получить работу, я бы не хотел работать на кого-то, кто заставлял меня за один дизайн над другим, потому что им сказали, что интерфейсы являются лучшими. Оба хороши в зависимости от ситуации, иначе почему язык решил добавить абстрактные классы? Разумеется, разработчики языка умнее меня.

Ответ 13

Это проблема "множественного наследования". Мы можем "расширять" не более одного класса abstarct за один раз через другой класс, но в интерфейсах мы можем "реализовать" несколько интерфейсов в одном классе. Итак, хотя Java не обеспечивает множественное наследование в целом, но с помощью интерфейсов мы можем включить в него свойство наследования множителя.

Надеюсь, это поможет!!!

Ответ 14

interface - более чистый способ написания чисто абстрактного класса. Вы можете сказать, что реализация не прокралась (конечно, вы можете сделать это на определенных этапах обслуживания, что делает интерфейсы плохими). Это об этом. Для клиентского кода практически нет различий.

JDBC - очень плохой пример. Спросите любого, кто попытался реализовать интерфейсы и сохранить код между выпусками JDK. JAX-WS еще хуже, добавив методы в выпуски обновлений.

Существуют технические отличия, такие как способность умножать интерфейс "наследовать". Это, как правило, является результатом запутанной конструкции. В редких случаях было бы полезно иметь иерархию реализации, которая отличается от иерархии интерфейса.

В обратном направлении для интерфейсов компилятор не может выбрать некоторые невозможные нажатия / instanceof s.

Ответ 15

Существует одна причина, не упомянутая выше.

Вы можете легко украсить любой интерфейс с помощью java.lang.reflect.Proxy, позволяющего добавлять пользовательский код во время выполнения к любому методу в данном интерфейсе. Это очень мощный.

См. http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html для учебника.

Ответ 16

интерфейс не заменяет абстрактный класс.

Предпочитают

интерфейс: Чтобы реализовать контракт несколькими несвязанными объектами

абстрактный класс: Чтобы реализовать одно и то же или другое поведение среди нескольких связанных объектов


Обратитесь к этому связанному вопросу SE для использования как интерфейса, так и абстрактного класса

Интерфейс против абстрактного класса (общий OO)

Случай использования:

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

Если вам нужно реализовать возможность для множества неперекрытых объектов, абстрактный класс не будет служить цели, и вы должны выбрать интерфейс.

Ответ 17

Вы можете реализовать несколько интерфейсов, но особенно с С# вы не можете иметь несколько наследований

Ответ 18

Поскольку интерфейсы не заставляют вас входить в иерархию наследования.

Ответ 19

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

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

Ответ 20

Хорошо, я бы предложил, чтобы сам вопрос был перефразирован. Интерфейсы заключаются главным образом в контрактах, которые приобретает класс, реализация этого договора будет меняться. Абстрактный класс обычно содержит некоторую логику по умолчанию, а его дочерние классы добавят еще немного логики. Я бы сказал, что ответ на вопросы зависит от проблемы с алмазами. Java предотвращает множественное наследование, чтобы избежать этого. (http://en.wikipedia.org/wiki/Diamond_problem).

Ответ 21

Они попросили меня взять любой из JDBC api что вы используете. "Почему они Интерфейсы?".

Мой ответ на этот конкретный вопрос:

SUN не знает, как реализовать их или что включить в реализацию. Своими поставщиками услуг /db для внедрения их логики в реализацию.

Конструкция JDBC имеет связь с шаблоном Bridge, в котором говорится: "Отмените абстракцию от ее реализации, чтобы они могли варьироваться независимо".

Это означает, что иерархия интерфейсов JDBC api может развиваться независимо от иерархии реализации, которую поставщик или использует поставщик jdbc.

Ответ 22

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

Одним хорошим примером является Java 6 SwingWorker. Он определяет структуру, чтобы сделать что-то в фоновом режиме, требуя от пользователя определить doInBackground() для реальной задачи.

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

public abstract class ProgressiveSwingWorker<T, V> extends SwingWorker<T, V> {

    private JFrame progress;

    public ProgressiveSwingWorker(final String title, final String label) {
        SwingUtilities.invokeLater(new Runnable() {
            @SuppressWarnings("serial")
            @Override
            public void run() {
                progress = new JFrame() {{
                    setLayout(new MigLayout("","[grow]"));
                    setTitle(title);
                    add(new JLabel(label));
                    JProgressBar bar = new JProgressBar();
                    bar.setIndeterminate(true);
                    add(bar);
                    pack();
                    setLocationRelativeTo(null);
                    setVisible(true);
                }};
            }
        });
    }

    /**
     * This method has been marked final to secure disposing of the progress dialog. Any behavior
     * intended for this should be put in afterProgressBarDisposed.
     */
    @Override
    protected final void done() {
        progress.dispose();
        try {
            afterProgressBarDisposed(get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    protected void afterProgressBarDisposed(T results) {
    }

}

У пользователя все еще есть требование обеспечить реализацию doInBackground(). Тем не менее, они также могут отслеживать поведение, например, открывать другое окно, отображать JOptionPane с результатами или просто ничего не делать.

Чтобы использовать его:

new ProgressiveSwingWorker<DataResultType, Object>("Editing some data", "Editing " + data.getSource()) {

    @Override
    protected DataResultType doInBackground() throws Exception {
        return retrieve(data.getSource());
    }

    @Override
    protected void afterProgressBarDisposed(DataResultType results) {
        new DataEditor(results);
    }

}.execute();

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

Ответ 23

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