#14 – Наследование в классах
Помимо классов и объектов, ООП содержит дополнительные три основные концепции: наследование, инкапсуляция и полиморфизм. Ранее мы уже знакомились с инкапсуляцией (закрытием данных) и теперь настал черед изучить наследование.
Видеоурок
Наследование является одним из ключевых понятий ООП. За счёт наследования можно создать один общий класс (класс родитель) и создать множество других классов (классы наследники), что будут наследовать все поля, методы и конструкторы из главного класса.
За чем использовать наследование?
Предположим что у нас есть один большой класс «Транспорт». В классе описываются базовые характеристики для всех транспортных средств:
- поля: скорость, вес, запас хода и тому подобное;
- методы: получение информации из полей, установка новых значений;
- конструктор: пустой и по установке всех полей.
На основе класса мы спокойно можем создать объект легковой машины, объект грузовика, объект самолета и так далее. У всех объектов будут одинаковые характеристики и методы.
Мы явно понимаем, что у объекта машина и самолёт будут разные поля и характеристики. Как можно поступить:
- Можно создать два отдельных класса: «Car» и «Airplane». В каждом классе будут все методы, поля и конструкторы повторно переписанные из класса «Транспорт», а также будут новые методы, что важны только для конкретного класса;
- Можно создать два класса наследника: «Car» и «Airplane». Оба класса будут наследовать всё от класса «Транспорт» и при этом будут содержать свои дополнительные функции. Таким образом повторения кода не будет и код станет меньше и чище.
Создание классов наследников
Для создания класса наследника требуется создать класс и указать наследование от главного класса.
Пример класса наследника:
// Обычный класс class Transport < protected float speed; >// Также обычный класс, но наследует данные от класса Transport class Auto extends Transport < private String model; Auto (float speed, String model) < this.speed = speed; this.model = model; System.out.println("Скорость: " + speed + ", модель - " + model); >> public class Main < public static void main(String[] args) < // Можем ссылаться не только на данные из класса Auto, // но также на данные из класса Transport Auto bmw = new Auto(250.5f, "X3M"); Auto mercedes = new Auto(270f, "C63 AMG"); >>
Как вы можете заметить, у нас есть одна общая переменная speed. Поскольку модификатор доступа у неё protected, то доступ к переменной мы имеем внутри класса Transport, а также внутри всех классов наследников.
Для создания наследования мы прописали ключевое слово extends после имени класса и указали какой класс будет родительским.
Если необходимо из класса наследника получить данные из родительского класса, то всегда можно обращаться к значению super .
Весь код будет доступен после подписки на проект!
Наследование в Java
В случае наследования в Java для указания родительского класса используется ключевое слово extends, которое следует после имени дочернего, т. е. производного, класса. После extends пишется родительский класс. Другими словами, «дочерний класс расширяет родительский».
В Java нет множественного наследования от классов, но есть множественное наследование интерфейсов.
В Java есть особенность, связанная с наследованием конструкторов и их вызовом. В конструкторе дочернего класса, перед тем как будут выполнены выражения тела, неявно вызывается дефолтный (без параметров) конструктор предка.
Причем конструктор без параметров обязательно должен быть в родительском классе, если в дочернем предполагаются только конструкторы с аргументами, а родительские конструкторы не вызываются явно. В этом случае, если в родительском классе будут только конструкторы с аргументами, то в дочернем будет возникать ошибка.
public class Main public static void main(String[] args) Child child = new Child(10); System.out.println(child.a); System.out.println(child.b); > >
class Parent int a; Parent(int a) this.a = 1; > >
class Child extends Parent int b; Child(int b) this.b = b; > >
В приведенном выше примере ошибка возникает на уровне конструктора класса Child. Перед тем как выполнится выражение this.b = b, будет неявно вызван конструктор Parent без параметров. Однако такого конструктора в родительском классе нет.
Если мы закомментируем или удалим конструктор Parent,
class Parent int a; // Parent(int a) // this.a = 1; // > >
то ошибка в дочернем классе исчезнет. Дело в том, что в Java любой класс есть потомок базового класса Object, у которого всегда имеется пустой дефолтный конструктор. В данном случае, двигаясь по дереву наследования к корню, из Child будет вызываться конструктор базового для всех класса.
Почему же он не вызывался, когда в Parent был определен конструктор с параметрами? В Java, как только вы определяете в классе любой конструктор, дефолтный родительского уже не наследуется напрямую, а начинает неявно вызываться из собственных конструкторов.
Заметим, что у объектов Child поле this.a существует, даже если ему не было присвоено значение. В данном случае значение будет нулевым.
Обычным вариантом недопущения подобных ошибок является объявление в Parent конструктора без аргументов. Он может быть пустым, или содержать какой-либо код:
import java.util.Random; class Parent int a; Parent() Random random = new Random(); this.a = random.nextInt(10); > >
Другой распространенный вариант – когда в дочернем классе явно вызывается конструктор родительского класса с помощью ключевого слова super. Причем вызывать всегда надо первой строчкой тела конструктора.
public class Main public static void main(String[] args) Child child = new Child(10, 20); System.out.println(child.a); System.out.println(child.b); > >
class Parent int a; Parent(int a) this.a = a; > >
class Child extends Parent int b; Child(int a, int b) super(a); this.b = b; > >
Обычные методы наследуются и переопределяются стандартно для объектно-ориентированного программирования. При этом в Java наследовать и переопределять можно только нестатические методы. Таким образом, если в родительском классе объявлен статический метод, то дочерние классы его не наследуют.
В Java объекты дочернего класса можно присваивать переменным родительского. Подобное называется расширением типа. При этом в случае вызова переопределенных методов будут вызываться методы дочернего класса, а не родительского. Однако набор допустимых для вызова методов определяется родительским классом, то есть типом переменной.
Также возможно сужение типа, когда объект, связанный с переменной родительского типа, присваивается переменной дочернего, то есть своего типа. В этих случаях, чтобы избежать ошибок, следует проверять тип объекта. Например, пусть класс B производный от класса A. Тогда:
A var = new B(); if (var instanceof B) B var1 = (B) var; >
Если в методе дочернего класса требуется использовать возможности родительского, то обращение ко второму происходит через слово super, после которого пишется необходимый метод.
public class ParentMeth public static void main(String[] args) ChildMeth ch = new ChildMeth(); ch.PrintStr(); > public void PrintStr() System.out.println("Parent class"); > > class ChildMeth extends ParentMeth public void PrintStr() super.PrintStr(); System.out.println("Child class"); > >
Parent class Child class
X Скрыть Наверх
Программирование на Java. Курс
Правила наследования в Java

Привет! В этой статье мы расскажем об одном из основополагающих принципов ООП, который Вам обязательно нужно понять — наследование.
Что такое наследование?
Наследование (англ. inheritance) — это механизм, позволяющий создавать классы на основе другого класса.
Так, один класс может «наследовать» характеристики другого — его методы и переменные.
- Класс, являющийся основой, называют: базовым, супер, родительским.
- Класс, который создают, называют: потомок, наследник или производный класс.
Пример
Представим, что у нас есть класс Dog. Что есть у каждой собаки? Четыре лапы, один хвост — и еще они умеют гавкать и вилять хвостом.
А теперь представим, что мы хотим создать классы для разных пород — овчарок, бульдогов и болонок.



У всех овчарок, бульдогов и болонок тоже будет четыре лапы и хвост, они тоже будут гавкать и вилять хвостом.
Конечно, мы можем просто брать и копировать эти методы и переменные в каждый класс. Но зачем? Мы можем использовать наследование.
Если мы сделаем все классы пород наследниками класса Dog, они будут иметь доступ ко всем его методам и переменным автоматически. Ну, почти ко всем.
Естественно, сама Джава тоже использует механизм наследования. Например, все классы языка наследуют класс Object, а классы ArrayList и LinkedList — класс List.
Как наследовать?
Наследование — это просто.
Для того, чтобы унаследовать класс, нужно использовать ключевое слово extends:
Урок 9. Основы языка JAVA. Наследование в java
Наследование в Java позволяет повторно использовать код одного класса в другом классе, то есть вы можете унаследовать новый класс от уже существующего класса. Главный наследуемый класс в Java называют родительским классам, или суперклассом. Наследующий класс называют дочерним классом, или подклассом. Подкласс наследует все поля и свойства суперкласса, а также может иметь свои поля и свойства, отсутствующие в классе-родителе.
Пример наследования
Рассмотрим класс под названием Shape (Форма). Shape является базовым классом, от которого наследуются другие формы, таких как прямоугольник, квадрат, круг и т.д.
public class Shape < public double area () < return 0; >>
Поскольку это просто общая «форма», метод вычисления площади area() будет возвращать ноль.
Чтобы узнать площадь конкретной фигуры, нужно создать подкласс, унаследованный от класса Shape, и в нем переопределить метод area() .
От класса Shape наследуется класс Circle, который тоже представляет собой форму.
class Circle extends Shape < // ключевое слово "extends" означает наследование private static final double PI = Math.PI; // константа private double diameter; //любое число, представляющее диаметр этого круга public Circle(double diameter) < // конструктор this.diameter = diameter; >public double area() < double radius = diameter / 2.0; return PI * radius * radius; >>
Метод area() базового класса наследуется классом Circle и становится доступен в нем, но нам нужно переопределить метод area() в классе Circle, таким образом, чтобы он вычислял площадь круга.
Преимущество использования наследования в том, что вы можете написать код, который можно применить к ряду классов, расширяющих более общий класс.
Создадим класс Main, и в нем напишем метод, который вычисляет большую площадь двух фигур:
public class Main < public static void main(String[] args) < Shape s1 = new Circle (5.0); Shape s2 = new Rectangle (5.0, 4.0); Shape larger = getLargerShape(s1,s2); System.out.println("The area of the larger shape is: "+larger.area()); >public static Shape getLargerShape(Shape s1, Shape s2) < if(s1.area() >s2.area()) return s1; else return s2; > >
Как вы можете видеть, метод getLargerShape() не требует указания определенного типа фигуры для его двух параметров. В качестве параметров для этого метода можно использовать экземпляр любого класса, который наследует тип Shape. Можно использовать экземпляр класса круг, прямоугольник, треугольник, трапеция, и т.д. – до тех пор, как они наследуют класс формы.
Резюме:
Наследование классов ( inheritance ) один из существенных атрибутов ООП (объектно-ориентированного программирования). Оно позволяет строить новые классы на базе существующих, добавляя в них новые возможности или переопределяя существующие.
Унаследованные поля могут быть использованы непосредственно, как и любые другие поля. Вы можете объявить поле в дочернем классе с тем же именем, что и в суперклассе, скрывая его таким образом (но это не рекомендуется). Вы можете объявить новые поля в подклассе, которых нет в суперклассе. Унаследованные методы тоже можно использовать непосредственно. Вы можете написать в подклассе новый метод, который имеет ту же сигнатуру, что и метод суперкласса, это называется переопределением метода. Вы можете написать новый статический метод в подклассе, который имеет ту же сигнатуру, что и метод суперкласса, таким образом, скрывая его (то есть метод суперкласа будет виден внутри подкласса только через ключевое слово super). Вы можете объявить новые методы в подклассе, которых нет в суперклассе. Вы можете написать конструктор подкласса, который вызывает конструктор суперкласса, неявно или с помощью ключевого слова super. Подкласс не наследует закрытые члены родительского класса, например, обозначенные модификатором private.
