🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
Привет! Сегодня мы рассмотрим один из принципов объектно-ориентированного программирования (ООП): наследование. А заодно познакомимся и с другими видами связей между классами: композицией и агрегацией.

Что ж, поехали!
Наследование в Java и его преимущества
Наследование – это механизм, позволяющий описать новый класс на основе существующего класса (родительского). При этом новый класс перенимает свойства и функциональность родительского класса.
Давайте вспомним пример наследования:
public class Car {
private String model;
private int maxSpeed;
private int yearOfManufacture;
public Car(String model, int maxSpeed, int yearOfManufacture) {
this.model = model;
this.maxSpeed = maxSpeed;
this.yearOfManufacture = yearOfManufacture;
}
public void gas() {
// Газ
}
public void brake() {
// Тормоз
}
}
public class Truck extends Car {
public Truck(String model, int maxSpeed, int yearOfManufacture) {
super(model, maxSpeed, yearOfManufacture);
}
}
public class Sedan extends Car {
public Sedan(String model, int maxSpeed, int yearOfManufacture) {
super(model, maxSpeed, yearOfManufacture);
}
}Run CodeУ нас есть определенная программа, которая предполагает работу с различными типами автомобилей. Даже если вы не являетесь автолюбителем, вы наверняка знаете, что в мире существует огромное количество типов автомобилей. 🙂 Поэтому общие свойства всех машин мы вынесли в родительский класс Car.
Итак, что же объединяет все машины, независимо от их разновидности?
У каждого автомобиля есть год выпуска, название модели и максимальная скорость. Эти характеристики мы разместили в полях model, maxSpeed и yearOfManufacture. Что касается поведения – любая машина умеет разгоняться и тормозить 🙂 Эти действия мы описали в методах gas() и brake().
Зачем нужно наследование?
Какие преимущества это нам дает? Прежде всего, это уменьшает количество кода.
Конечно, можно было бы обойтись без родительского класса. Но тогда в каждом классе Truck, Sedan, F1Car и SportsCar пришлось бы заново писать методы gas() и brake(). А еще создавать поля model, maxSpeed и yearOfManufacture. Представьте, сколько бы пришлось писать лишнего кода, если в проекте множество разных типов авто.

Когда классов автомобилей становится много, объём дублирующих фрагментов действительно начинает иметь значение.
Вынося общие поля и методы (их ещё называют состояния и поведения) в родительский класс, мы серьезно экономим и время, и место. А если какой-то тип машины имеет уникальные свойства или методы, которых нет у других? Нет проблем! Их можно спокойно добавить в дочерний класс.
Пример:
public class F1Car extends Car {
public void pitStop() {
// Только гоночные машины делают пит-стопы
}
public static void main(String[] args) {
F1Car formula1Car = new F1Car();
formula1Car.gas();
formula1Car.pitStop();
formula1Car.brake();
}
}Run CodeНа примере болида Формулы-1 видно: в отличие от других машин, у него есть особенное поведение – пит-стоп.
Это не нарушает структуру наследования: базовое поведение описано в родительском классе Car, а специфичные методы можно спокойно добавить в код дочернего класса. То же самое и с полями: если у дочернего класса появляются уникальные свойства, мы просто объявляем их внутри него – и все.

То же самое касается и полей: если у дочернего класса есть уникальные свойства, мы спокойно объявляем эти поля внутри дочернего класса и перестаем беспокоиться 🙂 Возможность повторного использования кода – главное преимущество наследования.
Для программиста особенно важно писать как можно меньше лишнего кода. С этим принципом вы еще не раз столкнетесь на практике.
Особенности наследования в Java
Еще одна важная вещь, которую нужно запомнить – в Java нет множественного наследования. Каждый класс может наследовать только один родительский класс. Почему так устроено, мы обсудим позже. Сейчас просто стоит это запомнить.
С наследованием все более-менее понятно. Давайте двигаться дальше.
Композиция и агрегация
Классы и объекты могут быть связаны друг с другом. Наследование описывает связь типа “является“. Например: лев — это животное. Такую связь легко выразить через наследование, где Animal – родительский класс, а Lion – дочерний.
Однако не все отношения описываются таким образом. Например, клавиатура определенно связана с компьютером, но это не компьютер. Руки как-то связаны с человеком, но являются человеком.
В таких случаях связь описывается по-другому: не “является“, а “имеет“. Рука не является человеком, но является частью человека. Клавиатура не является компьютером, но является его частью.
Такую “has-a” связь можно выразить в коде с помощью агрегации и композиции. Главное отличие между ними – в “строгости” этой связи.
Приведем простой пример: у нас есть класс Car. У каждого автомобиля есть двигатель. Кроме того, в каждом автомобиле есть пассажиры.
В чем принципиальная разница между движком Engine и полями пассажиров Passenger[]? Если в машине сидит пассажир А, это не мешает сидеть в ней пассажирам В и С. Машина может одновременно везти несколько пассажиров. Более того, если все пассажиры выйдут из автомобиля, он все равно будет работать без сбоев.
Отношения между классом Car и массивом Passenger[] менее строгие. Такую связь называют агрегацией.
Другой пример агрегации: предположим, у нас есть класс Student и класс StudentGroup. Студент может вступить в несколько студенческих организаций: клуб физиков, клуб фанатов “Звездных войн” и/или студенческий комедийный клуб.
Композиция – это более строгий вид отношений. Когда мы говорим о композиции, объект является частью другого объекта и не может одновременно принадлежать другому объекту того же типа.
Самый простой пример – двигатель автомобиля. Двигатель является частью автомобиля и не может быть частью другого автомобиля. Как видите, их отношения гораздо строже, чем отношения между Car и Passengers.

Теперь вы знаете, как классы могут быть связаны друг с другом с помощью наследования, композиции и агрегации. Эти механизмы помогают строить более гибкие и понятные программы.
Перевод статьи «Relationships between classes. Inheritance, composition, and aggregation».
