Каковы некоторые практические примеры абстрактных классов в Java? - PullRequest
17 голосов
/ 02 октября 2009

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

Ответы [ 11 ]

9 голосов
/ 02 октября 2009

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

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

5 голосов
/ 02 октября 2009

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

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

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

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

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

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

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

Классы Прямоугольник, Линия, Безье и Круг наследуется от GraphicObject

[...]

Источник: Java & trade; Учебники

5 голосов
/ 02 октября 2009

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

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

4 голосов
/ 02 октября 2009

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

Итак, что я считаю хорошим вариантом использования абстрактного класса? Один из моих любимых примеров - применение шаблона 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);
    }
}
3 голосов
/ 02 октября 2009

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

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

Как пример:

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

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

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

2 голосов
/ 02 октября 2009

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

public abstract class Instruction {

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

    abstract void firstStep();

    abstract void secondStep();

    abstract void thirdStep();

}
2 голосов
/ 02 октября 2009

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

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

2 голосов
/ 02 октября 2009

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

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

     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
     }

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

2 голосов
/ 02 октября 2009

http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html

http://java.sun.com/docs/books/tutorial/java/concepts/interface.html

Короче говоря, абстрактный класс может быть частично реализован, а интерфейс - нет. Подробнее в ссылках выше.

1 голос
/ 07 ноября 2016

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

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

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

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

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

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

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

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

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

Ссылка на этот пост:

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

...