Наследуются ли приватные переменные методы и другие свойства родителя его наследником

Обновлено: 30.06.2024

Наследование — это свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью.

Класс (объект), от которого производится наследование, называется базовым, родительским или суперклассом (объектом).

Новый класс (объект) — потомком, наследником, дочерним или производным классом (объектом).

Наследование позволяет сокращать код, на каждом иерархическом шаге учитывая только изменения, не дублируя всё остальное, учтённое на предыдущих шагах.

JavaScript вместо наследования использует прототипирование: если искомого свойства или вызванного метода в самом объекте нет, то запрос передаётся объекту-прототипу (свойство prototype всех объектов JavaScript). При этом объект, от которого произошло наследование называется прототипом, и унаследованные свойства могут быть найдены в объекте prototype конструктора. Поведение всех объектов класса можно поменять, заменив один из методов прототипа (например, добавив метода .toBASE64 для класса String ).

Процесс классического наследования должен создавать уровень абстракции, т.е. создается вертикальная иерархия.

При каждом наследовании, каждый дочерний класс должен снижать уровень абстракции, тем самым снижая уровень обобщения, например:

"ТУФЛИ" => "МУЖСКИЕ ТУФЛИ" => "МУЖСКИЕ ЛЕТНИЕ ТУФЛИ" => "МУЖСКИЕ ЛЕТНИЕ БЕЛЫЕ ТУФЛИ"

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

Объекты в классических объектно-ориентированных языках программирования могут быть созданы только путем создания экземпляров классов .

Задача программиста при использовании парадигмы классического наследования создать иерархию сущностей от максимальной общей к максимально конкретной.

При использование парадигмы прототипного наследования программист имеет дело только с объектами и при этом у него есть возможность создавать сущности в одном (горизонтальном) уровне абстракции .

В JavaScript выделяют:

  1. функциональное наследование (через функции-конструкторы и классы class - с особенностями);
  2. прототипное наследование.

Классовое наследование

Создание класса с использованием ключевого слова class фактически является формой функции-конструктора, поэтому классовое наследование может как использовать, так и не использовать ключевое слово class из ES6.

Класс похож на шаблон – описание объекта который будет создан. Экземпляры классов обычно создаются через функции-конструкторы с помощью ключевого слова new .

Классы наследуются от классов и создают дочерние связи: иерархическую классовую таксономию - самую сильную связанность доступную в объектной-ориентированной архитектуре.

Эти таксономии практически невозможно спроектировать правильно для всех новых сценариев. Широкое распространение (использование) базового класса приводит к проблеме "хрупкого базового класса" (сложность внесения правок, когда он начинает работать неправильно). Классовое наследование служит причиной многих проблем в ОО проектировании:

  1. проблема сильной связанности (классовое наследование является самым сильным связанным доступным в ОО проектировании);
  2. проблема хрупкого базового класса (неэластичная иерархия, в конечном итоге, все развивающиеся иерархии не будут работать для новых сценариев);
  3. дублирование кода (в связи с неэластичными иерархиями новые сценарии часто вклинены в нескольких местах, дублируя код);
  4. проблема "гориллы и банана" (вы хотели банан, но получили гориллу, ждущую банан, и целые джунгли в дополнение).

Прототипное наследование

Прототип - это экземпляр некоторого объекта, от которого наследуются напрямую другие объекты. Экземпляры могут быть скомпонованы из большого количества разных объектов, позволяя легко проводить точечное наследование и строить простую [[Prototype]] иерархию.

Функциональное наследование в JS

Функциональное наследование использует вызов родительской функции-конструктора внутри функции-конструктора дочернего класса с одновременной передачей ей в качестве контекста this текущего объекта:


Основная терминология

Для того, чтобы хорошо разбираться в дальнейшей информации, пользователю предстоит изучить некоторые термины:

Все это обязательно поможет каждому программисту. Предложенная терминология используется не только в Си-семействе, но и в остальных programming languages.

О классах C++

Немаловажный раздел – это классы и объекты. Они тесно связаны с так называемым наследованием, на котором будет заострено внимание далее.

Классом принято в C++ называть абстракции, которые описывают методы и свойства элементов, которых не существует. Объекты здесь – это конкретизированное представление абстракций со своими методами и свойствами.

Экземпляры класса – составляющие утилиты, которые появились, опираясь на один класс. У них может быть совершенно разное поведение и свойства. Построить оные удается несколькими способами. А именно:

Для того, чтобы класс объявить, требуется задействовать ключевое слово Class. У рассматриваемого элемента есть спецификаторы доступа:

Это базис, который пригодится каждому для углубленного изучения наследования.

Наследование Си Плюс-Плюс

Вот код, который поможет представить в приложении человека:

Внимание: программерам в процессе работы с рассматриваемым действием может потребоваться работа с include iostream. Это – элемент стандартной библиотеки в Си Плюс-Плюс, который представлен заголовочным файлом с классами, переменными и функциями.

На основании предложенного выше кода будем работать с наследованием в Си Плюс-Плюс. Требуется написать утилиту, отслеживающую сведения о тех, кто играет в баскетбол. Сохраняется средний уровень игры, а также то, сколько очков набрал конкретный человек:


Это – незавершенный public class. Дополнительно необходимо знать имя и возраст игрока. Для этого используется первый представленный код. Вывести запрашиваемую информацию предлагается несколькими способами:

После изучения возможных вариантов развития событий стоит отдать предпочтение последнему.

Для реализации поставленной задачи требуется после public class BasketballPlayer воспользоваться двоеточием. Здесь без ключевого слова public не обойтись, как и без имени классового элемента. Это – открытая операция:


Также программисты часто думают, что такое так называемое множественное наследование. Этот вопрос будет рассмотрен позже.

Полный код утилиты будет выглядеть так:


Public элемент Employer


Внимание: BasketballPlayer и Employer не связаны, но написаны в пределах одной и той же программы. Кодификация имеет следующий вид:


Цепочки


Информация берется из public Employer, который когда-то получал сведения из Human.

Важно учитывать, что:

  • вес;
  • сколько живет (в среднем);
  • наличие хвоста;
  • что ест.


Пусть будет некая классовая составляющая Degree, в котором выставлены градусы, а также метод, возвращающий его значение. Дочерним создадим Radiance. Он будет переводить информацию в радианы:


Внимание: запись public virtual делает информацию виртуальной.

Множественное наследование в C

Чтобы реализовать подход, потребуется:

В конечном итоге получится такой код:


Возможные неурядицы

Рассмотрим несколько наиболее распространенных ситуаций:


Для исправления ситуации достаточно внести уточнение:



Также стоит выяснить, что такое упомянутое ранее множественное наследование, но уже в Си Шарп. Работать, как и в прошлых случаях, будем с public, несмотря на то, что обычно переменные и другие составляющие бывают private.

Об интерфейсах

Но сначала обратим внимание на немаловажный момент – интерфейсы. Это инструмент реализации полиморфизма. Некий набор методов, обеспечиваемый классовыми составляющими, воплощающими в жизнь этот самый интерфейс.

Содержит сигнатуры. Не может включать в себя:

  • конструкторы;
  • константы;
  • поля;
  • статистические составляющие.

Объявляется и используется по следующему примеру:


Вот ситуация, при которой класс должен реализовывать интерфейс посредством предоставления реализации всех членов последнего:



Идея наследования в Java заключается в том, что вы можете создавать новые классы, основанные на существующих классах. Когда вы наследуете от существующего класса, вы можете повторно использовать методы и поля родительского класса. Более того, вы также можете добавлять новые методы и поля в ваш текущий класс.

Наследование представляет собой отношение IS-A, которое также известно как отношение родитель-ребенок.

Наследование в программировании на Java может быть определено как процесс, в котором один класс приобретает свойства (методы и поля) другого. С использованием наследования информация становится управляемой в иерархическом порядке.

Класс, который наследует свойства другого, известен как подкласс(производный класс, дочерний класс), а класс, свойства которого наследуются, называется суперклассом (базовый класс, родительский класс).

extends – это ключевое слово, используемое для обозначения наследования свойств класса.

Это важная часть ООП (объектно-ориентированного программирования).

Синтаксис наследования на Java

Пример наследования, здесь есть два класса, а именно: Calculation и My_Calculation.

Используя ключевое слово extends, My_Calculation наследует методы addition() и Subtraction() класса Calculation.

Скопируйте и вставьте следующую программу в файл с именем My_Calculation.java

Скомпилируйте и выполните приведенный выше код, как показано ниже.

После выполнения программы она даст следующий результат –
The sum of the given numbers:30
The difference between the given numbers:10
The product of the given numbers:200

В данной программе, когда создается объект класса My_Calculation, в нем создается копия содержимого суперкласса. Вот почему, используя объект подкласса, вы можете получить доступ к членам суперкласса.

наследование Java

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

Теперь создадим экземпляр класса, как указано ниже. Но используя ссылочную переменную суперкласса (в данном случае cal), вы не можете вызвать метод multiplication(), который принадлежит подклассу My_Calculation.

Подкласс наследует все члены(поля, методы и вложенные классы) от своего суперкласса. Конструкторы не являются членами, поэтому они не наследуются подклассами, но конструктор суперкласса может быть вызван из подкласса.

Ключевое слово super

Ниже приведены сценарии, в которых используется ключевое слово super.

Оно используется для различения членов суперкласса от членов подкласса, если они имеют одинаковые имена. И используется для вызова конструктора суперкласса из подкласса.

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

программа, которая демонстрирует использование ключевого слова super. У вас есть два класса, а именно Sub_class и Super_class, оба имеют метод display() с разными реализациями и переменную с именем num с разными значениями.

Мы вызываем метод display() обоих классов и печатаем значение переменной num обоих классов. Здесь вы можете заметить, что мы использовали ключевое слово super, чтобы отличать членов суперкласса от подкласса.

Скопируйте и вставьте программу в файл с именем Sub_class.java.

Скомпилируйте и выполните приведенный выше код, используя следующий синтаксис.

Результат
This is the display method of subclass
This is the display method of superclass
value of the variable named num in sub class:10
value of the variable named num in super class:20

Вызов конструктора суперкласса

Если класс наследует свойства другого класса, подкласс автоматически получает конструктор по умолчанию суперкласса. Но если вы хотите вызвать параметризованный конструктор суперкласса, вам нужно использовать ключевое слово super.
super(values);

Покажем как использовать ключевое слово super для вызова параметризованного конструктора суперкласса. Эта программа содержит суперкласс и подкласс, где суперкласс содержит параметризованный конструктор, который принимает целочисленное значение, и мы использовали ключевое слово super для вызова параметризованного конструктора суперкласса.

Скопируйте и вставьте следующую программу в файл с именем Subclass.java

Скомпилируйте и выполните приведенный выше код, используя следующий синтаксис.

Результат
The value of the variable named age in super class is: 24

IS-A отношения

IS-A – это способ показать: этот объект является типом этого объекта. Давайте посмотрим, как ключевое слово extends используется для наследования.

Теперь, на основе приведенного выше примера, в объектно-ориентированных терминах верно следующее:

  • Животное – суперкласс класса млекопитающих.
  • Животное – суперкласс рептилий.
  • Млекопитающее и Рептилия являются подклассами класса животных.
  • Собака является подклассом классов млекопитающих и животных.

Теперь, если мы рассмотрим отношения IS-A, мы можем сказать –

  • Млекопитающее IS-A животное
  • Рептилия IS-A животное
  • Собака IS-A млекопитающее
  • Отсюда: собака тоже животное

С использованием ключевого слова extends подклассы смогут наследовать все свойства суперкласса, за исключением частных свойств суперкласса.

Мы можем гарантировать, что млекопитающее на самом деле является животным с использованием оператора экземпляра.

И результат
true
true
true

Покажем как ключевое слово Implements используется для получения отношения IS-A.

Обычно ключевое слово Implements используется с классами для наследования свойств интерфейса. Интерфейсы никогда не могут быть расширены классом.

Что такое ключевое слово instanceof в Java?

Оператор java instanceof используется для проверки того, является ли объект экземпляром указанного типа (класс, подкласс или интерфейс).

Instanceof также известен как оператор сравнения типов, потому что он сравнивает экземпляр с типом. Возвращает либо true, либо false. Если мы применяем оператор instanceof к любой переменной, которая имеет нулевое значение, она возвращает false.

Если мы применяем оператор instanceof к переменной с нулевым значением, он возвращает false.

Давайте воспользуемся оператором instanceof, чтобы проверить, действительно ли млекопитающее является животным, а собака на самом деле животным.

Результат будет получен:
true
true
true

HAS-A отношения

HAS-A определяет, есть ли у определенного класса определенная вещь. Это соотношение помогает уменьшить дублирование кода и ошибок.

Это показывает, что класс Van имеет Speed(скорость). Имея отдельный класс скорости, нам не нужно помещать весь код, принадлежащий скорости, в класс Van, что позволяет повторно использовать класс скорости в нескольких приложениях.

В объектно-ориентированной функции на Java пользователям не нужно беспокоиться о том, какой объект выполняет работу. Для этого класс Van скрывает детали реализации от пользователей класса Van.

Пользователи просят класс Van выполнить определенное действие, и класс Van либо сделает работу сам по себе, либо попросит другой класс выполнить действие.

Типы наследования

Существуют различные типы наследования на Java, как показано ниже.

типы

Java не поддерживает множественное наследование. Это означает, что класс не может расширять более одного класса. Поэтому следующий код не сработает:

Однако класс может реализовать один или несколько интерфейсов, что помогло избавиться от невозможности множественного наследования.

Чтобы никто не мог наследовать класс просто используйте модификатор доступа final перед названием класса. (final class Nameclass <> )

Средняя оценка / 5. Количество голосов:

Спасибо, помогите другим - напишите комментарий, добавьте информации к статье.

Объект представляет собой некую абстрактную сущность. Допустим мы хотим создать класс "Animal" (Животное). Но животных же существует великое множество и вряд ли мы одним классом сможем описать их всех. В классе "Animal" мы можем создать поля и методы, присущие всем животным. Например, полями общими для всех животных могут быть "Вес", "Средняя продолжительность жизни", "Имеется хвост или нет". Методом может быть "eat()" (кушать), ведь все же животные питаются. От такого общего класса мы можем создать дочерний класс, который расширяет родительский класс. Например, класс "Dog" (собака) может расширять класс "Animal" уже конкретными полями и методами, которые соответствуют именно собакам.

Отношение наследования — это отношение перехода от более общей абстракции к более конкретной.

Немного поговорим про понятия, так или иначе связанные с наследованием.

Абстрактный класс — это класс, объекты которого нельзя создавать, т.е. нельзя будет использовать ключевое слово "new". Абстрактные классы используются при наследовании и используются как прародители к другим классам (реальным, не абстрактным). Т.е. в нашем примере можно класс "Animal" пометить как "abstract". Абстрактным может быть также и метод (это метод без реализации). Если в классе присутствует хотя бы один абстрактный метод, то и сам класс обязан быть абстрактным. В обратную сторону правило не действует.

С помощью ключевого слова "sealed" можно запретить создавать наследников от класса. Пример: класс "String". От этого класса мы не сможем создать наследников. Применение ключевого слова "sealed" к методу означает, что мы запрещаем переопределение этого метода в классах-наследниках.

Ключевое слово "virtual" применяется только к методам, и используется для того, чтобы превратить метод в виртуальный. Это делается для того, чтобы метод можно было переопределить в классах-наследниках. Переопределение метода означает, что мы внутри класса-наследника создаём метод, у которого заголовок полностью совпадает с заголовком метода класса-родителя. При этом в классе-наследнике нужно указать ключевое слово "override", чтобы явно указать, что мы переопределяем метод.

Давайте на примере разберем всё то, что мы только что узнали. Для простоты все поля и методы сделаем публичными (в реальных программах так, конечно, делать не нужно):

Конструкторы при наследовании.

Конструкторы не наследуются. Если в родительском классе определены различные конструкторы, то при наследовании эти конструкторы будут недоступны у класса-наследника. Несмотря на то, что конструкторы не наследуются — они автоматически вызываются. Когда вызывается конструктор класса-наследника автоматом вызывается конструктор класса-родителя. По умолчанию будет вызываться дефолтный конструктор класса-наследника без параметров. Обращаю ваше внимание на то, что если такого конструктора без параметров не будет, то будет получена ошибка при компиляции.

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

С помощью "base" также можно вызывать родительский метод.

Разберем на эту тему вот такой пример: пусть имеется родительский класс "Degree", имеющий одной поле "degrees" и один метод, который возвращает значение данного поля. Создадим дочерний класс "Radiance", который, используя метод родительского класса, возвращает градусы, переведенные в радианы:

Напоследок, обобщу особенности наследования:

  • Ключевые слова "sealed" и "static" (статический класс, про него поговорим в отдельной статье) запрещают наследование
  • Если в базовом классе определен какой-то метод "abstract", то базовый класс тоже должен быть абстрактным. В классе-наследнике такой абстрактный метод нужно переопределить. Абстрактный метод, по умолчанию, является виртуальным.
  • При проектировании программы важным является понимание того, что от чего можно унаследовать, а что нельзя. Для проверки условия наследования используется слово "является". В нашем примере: "Собака является животным? — является", "Питбуль является собакой? — является". А вот наоборот лучше не делать (технически конечно можно, но программы лучше сразу проектировать правильно), "Животное является собакой? — не является". Поэтому класс "Animal" от класса "Dog" наследовать нельзя.

На связи был Алексей Гулынин, оставляйте свои комментарии, увидимся в следующих статьях.

Что такое наследование?

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

Используя наследование, классы группируются вместе в иерархическую древовидную структуру. Более обобщенные классы появляются в корне иерархии с более конкретными классами, появляющимися на ветвях дерева. Эта классификация классов в связанные типы является причиной того, что наследование иногда называют обобщением (или генерализацией).

При использовании наследования все методы, свойства, события, индексаторы, операторы и некоторые внутренние переменные базового класса автоматически предоставляются каждому подклассу. Поскольку эта функция автоматически доступна в дочернем классе, нет необходимости дублировать код. Это уменьшает проблемы обслуживания, так как изменения должны быть сделаны только в одном классе, а не во всей серии связанных классов.

Полиморфизм

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

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

Одиночное и множественное наследование

Демонстрация наследования

Чтобы продемонстрировать использование наследования с объектами реального мира, в оставшейся части этой статьи мы создадим пример проекта, основанного на классе транспортного средства. Этот общий класс будет предоставлять методы и свойства, которые предоставляют все транспортные средства. Затем мы создадим подклассы транспортных средств с более специализированными функциональными возможностями.

Для начала создайте новое консольное приложение с именем "InheritanceDemo". Создайте файл класса с именем "транспортное средство" и добавьте следующий код к новому классу, чтобы предоставить свойство скорости и методы ускорения и замедления для всех транспортных средств:

private int _speed; public int Speed < get < return _speed; >> public void Accelerate(int mph) < _speed += mph; >public void Decelerate(int mph)

Новый класс транспортных средств может быть создан и испытан сам по себе. Чтобы убедиться, что класс работает правильно, мы можем изменить основной метод программы для проверки методов и свойства:

Создание производного класса

Синтаксис для объявления производного класса аналогичен синтаксису для любого другого стандартного класса. Чтобы указать базовый класс для наследования, его имя добавляется к объявлению подкласса, разделенному двоеточием (:) следующим образом:

Класс Мотороллеров

Теперь мы можем создать класс для представления механических транспортных средств, таких как автомобили, автобусы и т.д. Поскольку этот класс является специализированным транспортным средством, которое включает в себя все функциональные возможности класса транспортных средств, мы можем вывести этот класс из транспортного средства. Добавьте новый файл класса в проект и назовите его "MotorVehicle". Измените стандартное объявление класса, чтобы добавить отношение наследования.

Хотя новый класс не имеет кода, содержащегося в его блоке кода, он уже включает свойства и методы, определенные в его родительском классе. Мы можем легко продемонстрировать это, изменив основной метод программы следующим образом:

Новый метод main по существу совпадает с предыдущим примером, за исключением того, что используется экземпляр класса MotorVehicle, а не объект транспортного средства. Выходные данные программы идентичны предыдущему коду, поскольку используемая функциональность была унаследована без изменений.

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

Добавление своей функциональности в производный класс

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

Чтобы добавить свойства и метод, включите следующий код в класс MotorVehicle. Свойство FuelRemaining доступно только для чтения, поскольку его можно изменить только с помощью метода заправки, который возвращает количество галлонов топлива, добавленного для полного заполнения бака.

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

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

Переопределение функциональности базового класса

Наследование не ограничивается дополнительными функциями. Производные классы обычно изменяют поведение методов и свойств суперкласса в соответствии с использованием подкласса. В нашем примере все транспортные средства должны предоставлять возможность указать, когда они планируют повернуть налево или направо. Способ индикации будет варьироваться в зависимости от типа транспортного средства.

Добавьте следующий метод к классу транспортных средств:

Добавьте следующий метод в класс MotorVehicle:

Для велосипедов, велосипедист поднимет свою руку, чтобы указать, что они поворачиваются, поэтому добавьте следующий код к классу велосипедов:

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

Вызов функциональности базового класса

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

Это можно продемонстрировать, изменив метод индикации класса велосипедов. В следующем примере метод базового класса вызывается первым перед выполнением функциональных возможностей подкласса.

Вызов модифицированного метода показывает результаты обеих операций:

Полиморфизм и наследование

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

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

Чтобы продемонстрировать это, мы создадим некоторые основные переменные транспортного средства, но назначим им объекты MotorVehicle или Bicycle. Затем мы можем наблюдать результаты, как в следующем модифицированном основном методе:

Другой способ продемонстрировать такое поведение полиморфизма - использовать метод в классе программ. Метод, показанный ниже, принимает параметр транспортного средства, который может быть заменен мотоциклом или велосипедом.

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

Это явное приведение к более специализированному классу является противоположностью концепции восходящего преобразования, описанной ранее. Как таковой, он известен как downcasting.

Сокрытие имени

Сокрытие имен - это та же концепция, что и переопределение. При скрытии имени новая версия элемента или переменной создается в дочернем классе для замены или расширения родительской версии. Однако вместо использования ключевого слова override новое ключевое слово префиксирует объявление.

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

Скрытие имени полезно, если у вас нет контроля над базовым классом. Это может быть связано с тем, что он является частью библиотеки, для которой вы не владеете исходным кодом. Если элемент базового класса не помечен как виртуальный, то его нельзя переопределить. Однако даже невиртуальные члены могут быть скрыты.

В следующем примере метод Motorvehicle Indicate заменяется с помощью скрытия имени, а не переопределения. Обратите внимание, что эффект вызова метода отличается в зависимости от типа данных переменной, в которой находится объект. Чтобы продемонстрировать, сначала измените метод индикации класса MotorVehicle следующим образом:

Теперь измените основной метод и выполните его, чтобы увидеть результаты. Вы можете видеть, что выходные данные отличаются в каждом вызове метода Indicate, что-то, что не происходит с переопределением метода:

Примечание: после тестирования приведенного выше примера, верните метод индикации для использования переопределения, а не нового.

Защищенная область

Ранее в этом руководстве мы рассмотрели использование модификаторов открытого и частного доступа для изменения области переменных, методов, свойств и т. д. Чтобы резюмировать, элементы внутри классов, которые имеют общедоступную область видимости за пределами класса. Элементы, помеченные как частные, отображаются только в пределах области действия класса.

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

Чтобы продемонстрировать это, добавьте в класс MotorVehicle следующее свойство. Администрация отеля рассчитывает топливную экономичность автомобиля в милях на галлон с использованием расчетной базы, основанной на текущей скорости движения автомобиля.

Если вы попытаетесь скомпилировать приложение, вы получите три ошибки компилятора. Каждый указывает, что переменная "speed" недоступна из-за ее уровня защиты. Другими словами, поскольку "_speed" объявлен в классе транспортного средства как частная переменная, он не может использоваться в подклассе MotorVehicle. Однако, если вы измените переменную в классе транспортного средства, подлежащего защите, она будет видна подклассу, и программа будет компилироваться правильно.

Многоуровневые иерархии

Преимущества наследования не ограничиваются двухуровневыми иерархиями, которые были показаны в приведенных выше примерах. Можно добавить дополнительные подклассы для создания многоуровневого наследования. Это позволяет каждому наследнику добавлять более специализированную функциональность, сохраняя при этом членов всех своих родителей.

В случае с транспортными средствами, например, вполне вероятно, что вы захотите иметь отдельные классы для автомобилей, мотоциклов и автобусов. Каждый из них может наследовать от класса MotorVehicle для трехуровневой иерархии. Класс для грузовых автомобилей со свойством для их грузоподъемности может быть следующим:

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

Предотвращение наследования

В некоторых ситуациях может потребоваться предотвратить наследование. Например, вы можете создать класс для использования другими программистами, где класс содержит функции безопасности, которые не должны быть изменены. Чтобы указать, что класс не может быть унаследован, можно добавить перед определением класса ключевое слово sealed.

Для демонстрации измените объявление класса транспортного средства следующим образом:

Использование ключевого слова sealed предотвращает классы MotorVehicle и Bicycle от наследования функциональности от транспортного средства, поэтому приложение больше не компилируется. Вы также получаете предупреждение компилятора, подчеркивающее, что переменная "_speed" защищена. Это предупреждение появляется потому, что включение защищенной переменной в запечатанный класс предполагает, что была сделана ошибка. В любом случае защищенная переменная не может быть унаследована от запечатанного класса.

Заключение о событиях и наследовании

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

Читайте также: