Наследование (inheritance) - свойство системы, позволяющее описать (создать) новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью.
Класс, от которого производится наследование, называется базовым, родительским или суперклассом, а новый класс - потомком, наследником, дочерним или производным классом.
Используется для быстрой и безопасной организации родственных понятий: чтобы было достаточно на каждом иерархическом шаге учитывать только изменения, не дублируя всё остальное, учтённое на предыдущих шагах.
Общий смысл наследования заключается в том, что если
несколько классов имеют сходное поведение, то нет смысла дублировать их
описание (писать с нуля), лучше выделить у них общие признаки и
объединить их в общем для них родительском классе. При этом в описании
самих классов оставить только различающиеся элементы (методы, поля) либо
переопределить имеющиеся новой реализацией.
Для обозначения наследования в Java служит слово extends.
Так или иначе, но наследование всегда используется при создании любого класса, пусть и в не явном виде, т.к. любой класс в Java автоматически становится производным от суперкласса Object. Таким образом мы получаем доступ ко всем полям и методам этого класса.
Чем дальше вверх по иерархии наследования, тем более универсальными и абстрактными становятся классы. Такие классы становятся основой для других классов. И, как правило, запрещается создавать их экземпляры.
В Java есть два вида наследования:
- наследование классов: каждый наследник может иметь только одного родителя;
- наследование интерфейсов: интерфейс может иметь сколько угодно родителей.
Порядок инициализации объектов при наследовании
-
память, выделенная под новый объект, заполняется двоичными нулями;
-
в начале идет подъем до корня иерархии, а потом сверху вниз вызываются конструкторы один за другим вплоть до класса, конструируемого объекта;
-
инициализируются поля класса в порядке их записи;
-
вызывается тело конструктора нужного объекта.
Это делается для того, что бы быть уверенным, что все поля базовых классов проинициализированы, т.к. они доступны в нужном объекте, который имеет к ним доступ.
Ограничения
-
при наследовании доступ из методов класса-потомка к приватным полям родительского класса напрямую запрещен. Кроме того, данные поля не наследуются. Но благодаря специальным публичным методам, которые называются get/геттеры и set/сеттеры можно совершенно свободно обращаться к данным полям родительского класса из класса-потомка;
-
приватные методы, как и приватные поля также не наследуются. Это значит, что создание метода в классе-потомке с именем, аналогичным имени метода класса-предка — создаст совершенно новый метод и компилятор не предупредит вас об этом. Во избежание таких коллизий при наследовании методов желательно использовать аннотацию @Override. Благодаря данной аннотации компилятор сможет проконтролировать ваш код и выдать предупреждение, если переопределяемый метод не будет найден в родительском классе.
Достоинства
-
способствует уменьшению повторяемости кода, т.е. имеет место быть его переиспользование (англ. code reuse);
-
ускоряет разработку. тк наследование позволяет взять готовый класс, "клонировать" его в новый класс-потомок, т.е. получить весь функционал класса-предка, а затем расширить его, добавив новые методы и поля;
Недостатки
-
большое значение имеет правильное построение иерархии классов. Т.к на поздних этапах разработки, когда иерархия классов построена и на её основе разработано большое количество кода, оказывается трудно или даже невозможно внести какие-либо изменения в код базовых классов иерархии;
-
при внесении изменений в базовые классы — классы наследники об этом могут ничего не знать;
-
данный механизм требует, чтобы точный тип объекта был известен уже на стадии компиляции, что делает код, зависящим от реализации;
-
подкласс зависит от реализации родительского класса, что делает код сильно связанным.
Вместо наследования можно использовать композицию / агрегацию, т.к. этот механизм более гибок, так как позволяет динамически выбирать тип.
Когда нужно применять наследование
Для того что бы определить стоит ли применять наследование нужно для предка и предполагаемого производного класса попробовать установить отношение "является" ("is a").
Отношение "является" служит признаком наследования.
Наследование применяют для описания объектов, незначительно отличающихся друг от друга
При наследовании в классе-потомке должна добавляться функциональность, отличающаяся от существующей в классе-предке. Если в наследнике часть функциональности убирается, то это является поводом задуматься о необходимости в данной ситуации наследования.