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

Каковы некоторые практические примеры абстрактных классов в java?

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

4b9b3361

Ответ 1

Абстрактные классы являются "полуисполнениями" класса. Они могут быть частично реализованы с использованием некоторых общих функций, но оставить часть реализации наследующим классам. У вас может быть абстрактный класс с именем Animal, который реализовал некоторые общие поведения/значения, такие как Age, Name, SetAge(...). Вы также можете использовать методы, которые не реализованы (они abstract), очень похожи на интерфейс.

Интерфейсы - это просто контракты, которые определяют поведение, которое должно быть доступно для класса. У вас может быть такой интерфейс, как IWalker, который требует общедоступного метода Walk(), но не содержит каких-либо особенностей его реализации.

Ответ 2

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

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

Ответ 3

Абстрактные классы против интерфейсов

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

Несколько интерфейсов могут быть реализованы классами в любом месте иерархии классов, независимо от того, связаны ли они друг с другом каким-либо образом. Подумайте о Comparable или Cloneable, например.

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

Пример абстрактного класса

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

Classes Rectangle, Line, Bezier, and Circle inherit from GraphicObject

Классы Rectangle, Line, Bezier и Circle наследуются от GraphicObject

[...]

Источник: Учебные руководства Java ™

Ответ 4

Удивительно, но многие примеры/объяснения, приведенные здесь, не дают хороших аргументов в пользу использования абстрактного класса. Простое размещение общих полей/методов в суперклассе не требует абстрактного. Также (напыщенная речь), стыд над якобы знающими инженерами, которые все еще придумывают иерархии Animal/Vehicle/Figure, чтобы "объяснить" объектно-ориентированные концепции. Эти типы примеров очень вводят в заблуждение, потому что они указывают на неправильное направление; вы, как правило, НЕ должны поощрять прямое подклассирование, поскольку оно создает очень плотную связь между классами. Скорее используйте сотрудничество (rant заканчивается).

Итак, что я считаю хорошим примером для абстрактного класса? Одним из моих любимых примеров является применение шаблона GoF шаблона шаблона. Здесь вы хотите указать общий поток алгоритма один раз, но разрешить несколько реализаций отдельных шагов. Вот пример, который я только что собрал VirusScanEngine, содержащий основной алгоритм антивирусного сканирования (найдите следующий вирус, удалите или сообщите об этом, продолжайте до завершения сканирования) и LinearVirusScanner, который реализует необходимые шаги алгоритма (findVirus, deleteVirus и reportVirus). Приносим извинения всем разработчикам, действительно работающим над программным обеспечением для сканирования вирусов для этого ужасающего упрощения.

import java.util.Arrays;

public abstract class VirusScanEngine {

    public static void main(String[] args) {

        byte[] memory = new byte[] { 'a', 'b', 'c', 'M', 'e', 'l', 'i', 's', 's',
                'a' , 'd', 'e', 'f', 'g'};
        System.out.println("Before: " + Arrays.toString(memory));
        new LinearVirusScanner().scan(memory, Action.DELETE);
        System.out.println("After: " + Arrays.toString(memory));
    }

    public enum Action {
        DELETE, REPORT
    };

    public boolean scan(byte[] memory, Action action) {

        boolean virusFound = false;
        int index = 0;
        while (index < memory.length) {

            int size = findVirus(memory, index);
            if (size > 0) {
                switch (action) {

                case DELETE:
                    deleteVirus(memory, index, size);
                    break;
                case REPORT:
                    reportVirus(memory, index, size);
                    break;
                }
                index += size;
            }
            index++;
        }
        return virusFound;
    }

    abstract int findVirus(byte[] memory, int startIndex);

    abstract void reportVirus(byte[] memory, int startIndex, int size);

    abstract void deleteVirus(byte[] memory, int startIndex, int size);
}

и

public class LinearVirusScanner extends VirusScanEngine {

    private static final byte[][] virusSignatures = new byte[][] {
            new byte[] { 'I', 'L', 'O', 'V', 'E', 'Y', 'O', 'U' },
            new byte[] { 'M', 'e', 'l', 'i', 's', 's', 'a' } };

    @Override
    int findVirus(byte[] memory, int startIndex) {

        int size = 0;
        signatures: for (int v = 0; v < virusSignatures.length; v++) {

            scan: {
                for (int t = 0; t < virusSignatures[v].length; t++) {

                    if (memory[startIndex + t] != virusSignatures[v][t]) {
                        break scan;
                    }
                }
                // virus found
                size = virusSignatures[v].length;
                break signatures;
            }
        }
        return size;
    }

    @Override
    void deleteVirus(byte[] memory, int startIndex, int size) {

        for (int n = startIndex; n < startIndex + size - 1; n++) {
            memory[n] = 0;
        }
    }

    @Override
    void reportVirus(byte[] memory, int startIndex, int size) {

        System.out.println("Virus found at position " + startIndex
                + " with length " + size);
    }
}

Ответ 5

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

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

В качестве примера:

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

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

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

Ответ 7

Интерфейс не содержит никакой реализации.

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

     public abstract class Figure {
        protected Point position;

        public abstract void draw();
     }

     public class Square extends Figure {
       // position is usable

       public void draw() {
         // this method must be implemented, for Square not to be abstract
       }

       // here is other code
     }

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

Ответ 8

Если вам нужно обернуть голову концепцией абстрактных классов, взгляните на инструментарий Swing UI (или на AWT) в стандартной библиотеке.

Поскольку вы можете представить, что можно визуализировать (например, кнопку, метку), легко противопоставить ее тем вещам, которые невозможно создать (например, JComponent).

Ответ 9

Вы можете ограничить порядок выполнения команды конкретными шагами, но разрешить делегирование для поведения каждого шага:

public abstract class Instruction {

    void perform() {
        firstStep();
        secondStep();
        thirdStep();
    }

    abstract void firstStep();

    abstract void secondStep();

    abstract void thirdStep();

}

Ответ 10

Когда и зачем следует использовать абстрактные классы?

Ниже приведен ответ на ваш вопрос:

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

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

Если вы хотите поделиться кодом между несколькими близкородственными классами

Один практический пример: реализация метода шаблона в классе JDK Reader.java

Посмотрите ниже сообщение:

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

В чем разница между абстрактными классами и интерфейсами?

Обратитесь к этому сообщению:

Как я должен объяснить разницу между интерфейсом и абстрактным классом?

Ответ 11

В моем собственном мышлении правильный подход заключается в том, чтобы подходить к этому вопросу, чтобы определить контекст, с которым вы имеете дело в отношении объектов домена или сущности, а именно, каков ваш прецедент?

В моем личном опыте я делаю это, используя подход сверху вниз, а затем снизу вверх. Я начинаю искать наследование, рассматривая пример использования и видя, какие классы мне понадобятся. Затем я смотрю, есть ли superClassOrInterfaceType (так как оба класса и интерфейсы определяют типы, я объединять их в одно слово для простоты. Надеюсь, это не делает его более запутанным) объект домена, который будет охватывать все объекты, как, например, в классе superClassOrInterfaceType, если я работаю над прецедентом, использующим subtypeClassOrInterfaceTypes, например: автомобили, грузовики, джипы и мотоциклы. Если есть отношение иерархии, тогда я определяю superClassOrInterfaceType и subtypeClassOrInterfaceTypes.

Как я уже сказал, я обычно делаю сначала поиск общего класса superClassOrInterfaceType для объектов, с которыми я имею дело. Если это так, я ищу общие методы операций между subtypeClassOrInterfaceTypes. Если нет, я смотрю, есть ли общие реализации методов, потому что даже если у вас может быть superClassOrInterfaceType и могут иметь общие методы, реализации могут не поддерживать повторное использование кода. На данный момент, если у меня есть общие методы, но нет общих реализаций, я склоняюсь к интерфейсу. Однако, с этим упрощенным примером, у меня должны быть некоторые распространенные методы с некоторыми общими реализациями между подтипом транспортного класса subClassOrInterfaceTypes, который я могу повторно использовать с помощью.

С другой стороны, если нет структуры наследования, я начинаю снизу вверх, чтобы увидеть, есть ли общие методы. Если нет общих методов и нет общих реализаций, я выбираю конкретный класс.

Как правило, если есть наследование с общими методами и распространенными реализациями и необходимость в нескольких методах реализации подтипа в том же подтипе, то я иду с абстрактным классом, что редко, но я его использую. Если вы просто идете с абстрактными классами только потому, что есть наследование, вы можете столкнуться с проблемами, если код сильно изменится. Это подробно описано в примере здесь: Интерфейсы против абстрактных классов в Java для разных типов доменных объектов двигателей. Один из них требовал двухмоторный двигатель, который требовал использования нескольких методов реализации подтипа для использования в одном классе подтипа. Другими словами, одному классу нужны были методы реализации как от двигателей с солнечной батареей, так и от батарей, которые моделируют поведение, а не то, что вы хотите делать с абстрактными классами.

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

Вот некоторые ссылки, которые в большей степени относятся к этому вопросу.

Спасибо Тип и Нежный класс

Магия за подтиповым полиморфизмом

Максимизировать гибкость с интерфейсами и абстрактными классами

Интерфейсы против абстрактных классов в Java