Для объектно-ориентированного программирования абстракция — одна из его основных особенностей. В Java абстракция ООП может быть воплощена в двух формах:интерфейсы и абстрактные классы.Между ними много общего, а также много различий. Многие новички думают, что их можно использовать взаимозаменяемо, но на самом деле это не так. Сегодня давайте вместе изучим интерфейсы и абстрактные классы в Java.


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

Прежде чем понять абстрактные классы, давайте сначала разберёмся с абстрактными методами. Абстрактный метод — это особый метод: он имеет только объявление, но не имеет конкретной реализации. Формат объявления абстрактного метода таков:

abstract void fun();

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

Обратите внимание на следующий вопрос: в книге "Thinking in Java" абстрактный класс определён как "класс, содержащий абстрактные методы", но позже было обнаружено, что если класс не содержит абстрактных методов и только модифицирован с помощью abstract, он также является абстрактным классом. То есть абстрактный класс не обязательно должен содержать абстрактные методы. Лично я считаю, что это педантичный вопрос, потому что если абстрактный класс не содержит никаких абстрактных методов, зачем проектировать его как абстрактный класс? Так что пока просто запомните это понятие, и нет необходимости углубляться в то, почему.

[public] abstract class ClassName {
    abstract void fun();
}

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

Класс, содержащий абстрактные методы, называется абстрактным классом, но это не означает, что абстрактный класс может иметь только абстрактные методы. Как и обычный класс, он также может иметь переменные-члены и обычные методы-члены. Обратите внимание, что между абстрактными классами и обычными классами есть три основных различия:

  • 1) Абстрактные методы должны быть public или protected (потому что, если они private, они не могут быть унаследованы подклассами, и подклассы не смогут их реализовать). По умолчанию они public.
  • 2) Абстрактные классы нельзя использовать для создания объектов;
  • 3) Если класс наследует абстрактный класс, подкласс должен реализовать абстрактные методы родительского класса. Если подкласс не реализует абстрактные методы родительского класса, подкласс также должен быть определён как абстрактный класс.
  • В остальном абстрактные классы ничем не отличаются от обычных классов.

2. Интерфейсы

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

[public] interface InterfaceName {
 
}

Интерфейсы могут содержать переменные и методы. Однако учтите, что переменные в интерфейсе неявно определяются как переменные public static final (и могут быть только переменными public static final; использование private вызовет ошибку компиляции), а методы неявно определяются как методы public abstract и могут быть только методами public abstract (использование других ключевых слов, таких как private, protected, static, final и т. д., вызовет ошибку компиляции). Более того, ни один метод в интерфейсе не может иметь конкретной реализации; то есть все методы в интерфейсе должны быть абстрактными. Из этого мы можем приблизительно увидеть разницу между интерфейсами и абстрактными классами. Интерфейс — это чрезвычайно абстрактный тип; он более «абстрактный», чем абстрактный класс, а переменные в интерфейсе обычно не определяются.

Чтобы класс реализовал определённый набор интерфейсов, нужно использовать ключевое слово implements. Конкретный формат следующий:

class ClassName implements Interface1,Interface2,[....]{
}

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


Различия между абстрактными классами и интерфейсами

1. Синтаксические различия

  • 1) Абстрактные классы могут предоставлять детали реализации методов-членов, тогда как интерфейсы могут иметь только методы public abstract;
  • 2) Переменные-члены в абстрактных классах могут быть различных типов, тогда как переменные-члены в интерфейсах могут быть только типа public static final;
  • 3) Интерфейсы не могут содержать статические блоки кода или статические методы, тогда как абстрактные классы могут иметь статические блоки кода и статические методы;
  • 4) Класс может наследовать только один абстрактный класс, но класс может реализовывать несколько интерфейсов.

2. Различия на уровне проектирования

1) Абстрактный класс — это абстракция вида вещей, то есть абстракция класса, тогда как интерфейс — это абстракция поведения. Абстрактный класс абстрагирует весь класс как единое целое, включая атрибуты и поведение, но интерфейс абстрагирует только часть класса (поведение). Для простого примера, самолёты и птицы — это разные виды вещей, но у них есть общая характеристика: они оба умеют летать. При проектировании вы можете спроектировать самолёт как класс Airplane, а птицу как класс Bird, но вы не можете рассматриватьполёткак класс, потому что это всего лишь поведенческая особенность, а не абстрактное описание вида вещей. На этом этапе вы можете спроектировать полёт как интерфейс Fly, содержащий метод fly(), а затем Airplane и Bird реализуют интерфейс Fly в соответствии со своими потребностями. Что касается разных видов самолётов, таких как истребители и гражданские самолёты, они могут напрямую наследовать Airplane. Аналогично для птиц: разные виды птиц могут напрямую наследовать класс Bird. Из этого видно, что наследование — это отношение "является" (is a), тогда как реализация интерфейса — это отношение "имеет" (has a). Если класс наследует абстрактный класс, то подкласс должен быть разновидностью абстрактного класса, тогда как реализация интерфейса — это вопрос о том, обладает ли он или имеет ли он определённую возможность. Например, может ли птица летать (или обладает ли она характеристикой полёта); если она может летать, она может реализовать этот интерфейс, если не может летать, она не реализует этот интерфейс.

2) Уровни проектирования различаются. Абстрактный класс как родительский класс многих подклассов — это шаблонный дизайн. Интерфейс — это спецификация поведения, радиальный дизайн. Что такое шаблонный дизайн? Простейший пример: все использовали шаблоны в PPT. Если ppt B и ppt C спроектированы с использованием шаблона A, общая часть ppt B и ppt C — это шаблон A. Если их общую часть нужно изменить, достаточно изменить только шаблон A, и не нужно снова изменять ppt B и ppt C. Радиальный дизайн: например, если в каком-то лифте установлена определённая сигнализация, то как только сигнализацию нужно обновить, все они должны быть обновлены. То есть для абстрактного класса, если нужно добавить новый метод, можно напрямую добавить конкретную реализацию в абстрактный класс, и подклассы не нуждаются в изменениях; но с интерфейсом дело обстоит не так. Если интерфейс изменяется, все классы, реализующие интерфейс, должны быть соответственно изменены.

Ниже приведён самый распространённый в Интернете пример: пример дверей и сигнализаций: все двери имеютopen()иclose()два действия. На этом этапе мы можем определить это абстрактное понятие через абстрактные классы и интерфейсы:

abstract class Door { public abstract void open(); public abstract void close(); }

Или:

interface Door { public abstract void open(); public abstract void close(); }

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

1) Поместите все три функции в абстрактный класс, но таким образом все подклассы, наследующие этот абстрактный класс, будут иметь функцию сигнализации, однако некоторые двери могут не обязательно иметь функцию сигнализации;

2) Поместите все три функции в интерфейс. Тогда классу, которому нужна функция сигнализации, придётся реализовать open() и close() в этом интерфейсе, но, возможно, у этого класса вообще нет функций open() и close(), например у пожарной сигнализации.

Из этого видно, что open(), close() и alarm() класса Door фактически относятся к поведению двух разных категорий. open() и close() — это неотъемлемые поведенческие характеристики самой двери, тогда как alarm() — это расширенное дополнительное поведение. Поэтому наилучшее решение — спроектировать alarm отдельно как интерфейс, содержащий поведение alarm(), а Door — как отдельный абстрактный класс, содержащий два поведения open и close. Затем спроектировать класс AlarmDoor, который наследует класс Door и реализует интерфейс Alarm.

interface Alram { void alarm(); } abstract class Door { void open(); void close(); } class AlarmDoor extends Door implements Alarm { void oepn() { //.... } void close() { //.... } void alarm() { //.... } }

Исходный адрес: http://www.cnblogs.com/dolphin0520/p/3811437.html