Удивительно, но многие примеры / объяснения, приведенные здесь, не дают хороших аргументов для использования абстрактного класса. Простое размещение общих полей / методов в суперклассе не требует его абстрактности. Кроме того (начните разглагольствовать), позор предположительно хорошо осведомленным инженерам, которые все еще придумывают иерархии 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);
}
}