Кофе-брейк #95. Как решить проблему множественного наследования в Java
Источник: FreeCodeCamp Java — один из самых популярных объектно-ориентированных языков программирования, используемых сегодня. Поскольку он не зависит от платформы, вы можете найти Java-приложения на всех типах устройств и в каждой операционной системе. А поскольку Java относительно легко выучить, это один из языков, который осваивают многие программисты. Важная особенность Java, с которой вы должны быть знакомы, — это наследование классов. Наследование позволяет оптимизировать код, облегчая повторное использование классов. Когда вы можете повторно использовать код, который уже был протестирован и отлажен, жизненный цикл разработки программного обеспечения становится короче и менее затратным. Хотя теоретически это простая концепция, кодирование отношений наследования требует внимания к деталям. Особенно в отношении множественного наследования, когда один дочерний класс наследует свойства от нескольких родительских классов. Java отвергает множественные отношения наследования, потому что они создают неоднозначность, но есть несколько способов добиться многих из тех же эффектов, если вы знаете, что делать. В этой статье мы рассмотрим проблемы с множественным наследованием и обсудим альтернативные варианты кодирования на Java.
Терминология наследования
- Классы — это фундаментальная структура шаблона в объектно-ориентированных языках программирования. Класс определяет общие свойства для группы объектов.
- Родительский класс : также известный как базовые классы или суперклассы. Родительский класс — это расширяемый класс, который предоставляет функции дочернему классу. Он допускает возможность повторного использования. Определения и функции родительского класса повторно используются при создании дочерних классов.
- Дочерний класс : более обобщенно называемый подклассом, дочерний класс наследует функции от другого класса. Дочерние классы — это расширенные или производные классы.
- Наследование : отношения между родительским и дочерним классами.
Типы наследования ООП
- Одноуровневое наследование : когда дочерний класс наследует функции от единственного родительского класса.
- Многоуровневое наследование : это многоуровневая форма одноуровневого наследования. При многоуровневом наследовании дочерний класс также может выступать в качестве родительского класса для других дочерних классов. Отношения между каждым уровнем линейны — никакие ветви не выходят выше, чем при множественном наследовании. В этом случае конечный дочерний класс имеет функции со всех уровней выше.
- Иерархическое наследование : противоположность множественного наследования. В иерархическом наследовании единственный родительский класс имеет более одного дочернего класса. Таким образом, вместо того, чтобы иметь ветви над ним, он разветвляется внизу.
- Гибридное наследование : как следует из названия, гибридное наследование представляет собой комбинацию других типов наследования.
- Множественное наследование : при множественном наследовании дочерний класс имеет более одного родительского класса. Хотя Java и JavaScript не поддерживают множественное наследование, такие языки ООП, как C ++, поддерживают.
- Многопутевое наследование : гибрид множественного, многоуровневого и иерархического наследования, при многопутевом наследовании дочерний класс наследует свои характеристики и функции от родительского класса и нескольких дочерних классов родительского класса. Поскольку многопутевое наследование основано на множественном наследовании, Java не поддерживает его использование.
Почему Java не поддерживает множественное наследование
Основная проблема множественного наследования заключается в том, что оно может создавать неоднозначности в дочерних классах. В обзорном техническом документе 1995 года ведущий дизайнер Java Джеймс Гослинг заявил, что проблемы с множественным наследованием были одной из причин создания Java. Сложности, присущие множественному наследованию, наиболее отчетливо видны в проблеме алмаза. В задаче “ромб” родительский класс A имеет два различных дочерних класса B и C; то есть дочерние классы B и C расширяют класс A. Теперь мы создаем новый дочерний класс D, который расширяет как класс B, так и класс C. Обратите внимание, что у нас есть множественное наследование (D расширяет B и C), иерархическое наследование (B и C расширяют A) и многоуровневое наследование (D расширяет A, B и C). В проблеме ромба дочерние классы B и C наследуют метод от родительского класса A. И B, и C переопределяют унаследованный метод. Но новые методы в B и C противоречат друг другу. Окончательный дочерний класс D наследует два независимых и конфликтующих метода от своих нескольких родителей B и C. Неясно, какой метод класса D следует использовать, поэтому возникает двусмысленность. Другие языки программирования ООП реализуют различные методы решения неоднозначности множественного наследования.
Inheritance
Inheritance is a process where one class can inherit visible properties and methods from another class — the parent-child relationship between two classes (or superclass and subclass).
In the above example, the Student class extends the Person class so our Student class is a child class of Person class. Student class will extend all visible (depends on access modifiers of variables and methods) variables and methods.
Inheritance is useful for code reusability for example we can have one generic class that will have common properties and methods with default behaviors and the child classes can just extend it and reuse a lot of code. If the child class wants to have its own implementation for methods defined in the parent class, we can always override these methods in the child class.
One good example is java.lang.Object class. Object class is the parent class for all classes in java. Java automatically will inject extends Object syntax after every class declaration. Why every class needs to extend from super java.lang.Object class? So from every class in java, we can potentially create an object. It can be Person or it can be Student or it can be Car and so on and if we think about these classes they are all objects. Java wants to give generic behaviors for every object that ever will be created in java. The java.lang.Object has 11 methods(Java 8) so every class will inherit these methods.
This is one of the methods that will come from the Object class. We need the equals method to compare two objects of the same class on equality. So our superclass is giving as equals method to do so. By default, it will not compare two object properties, it will compare if two references are pointing to the same object or not(same as ==). We need to override the equals method and write the logic of how exactly we want to compare our objects.
It’s good to have some common methods for all objects because other libraries can assume that in order to compare your objects they can use the equals method. The same logic for other methods as well.
We are saying that every class extends Object but in this example, our Student class extends our Person class, not the Object class. Yes, the Student class will extend the Object class via the Person class.
Java allows only a single inheritance type. Multiple classes can inherit from a single class but one class cannot inherit multiple classes at the same time.
Method overriding and variables hiding
When a child class wants to use its own implementation of the method instead of the parent class method, it can always override it.
- in the above example, the child class BMW overrides the method of a parent class.
- @Override annotation will check if we are really overriding the method. If it finds some issues, it gives a compiler error.
Rules of overriding:
- Method name and number, order, and type of arguments should be exactly the same as the parent’s method.
- The return type should be the same or covariant with the parent method.
- The access modifier should be the same or more visible than the parent method.
- If exception declaration exists in the parent method, the child method can have the same type of exception declaration or a smaller type.
Why do some people refer to overriding as runtime polymorphism?
In the above example, we have Car as our reference type and it will decide what method and variables are available for this reference. The actual object of car is BMW . During the compilation, java thinks that it will call drive() method from Car class, but during the runtime, it figures out that BMW actually overrides the method and it will call the method from BMW
Now, for variables, if child class will have variables with exact same name as parent class does. Child class variables will hide the parent class variables.
Hiding is different than overriding, there is no runtime polymorphism.
this and super keywords
this keyword is used to refer to the current object of a class.
super keyword is used to refer to the parent class members.
- As you can see in the printName() method we are using super to get the name of a parent class.
- It works similarly with methods.
To call a constructor we can also use this and for parent class constructor super . Let’s see examples of calling empty constructors.
Родительские и дочерние классы в Java
Java поддерживает наследование, концепцию ООП, когда один класс получает члены (методы и поля) другого.
Вы можете наследовать члены одного класса от другого, используйте ключевое слово extends как:
Класс, который наследует свойства другого, известен как дочерний класс (производный, подкласс), а класс, свойства которого наследуются, известен как родительский класс (базовый, суперкласс).
Ниже приведен пример, демонстрирующий наследование. Здесь у нас есть два класса, а именно Sample и MyClass. Где Sample является родительским классом, а класс с именем MyClass является дочерним классом в Java.
Пример
Средняя оценка 0 / 5. Количество голосов: 0
Спасибо, помогите другим — напишите комментарий, добавьте информации к статье.
Или поделись статьей
Видим, что вы не нашли ответ на свой вопрос.
Помогите улучшить статью.
Напишите комментарий, что можно добавить к статье, какой информации не хватает.
Урок 9. Основы языка JAVA. Наследование в java
Наследование в Java позволяет повторно использовать код одного класса в другом классе, то есть вы можете унаследовать новый класс от уже существующего класса. Главный наследуемый класс в Java называют родительским классам, или суперклассом. Наследующий класс называют дочерним классом, или подклассом. Подкласс наследует все поля и свойства суперкласса, а также может иметь свои поля и свойства, отсутствующие в классе-родителе.
Пример наследования
Рассмотрим класс под названием Shape (Форма). Shape является базовым классом, от которого наследуются другие формы, таких как прямоугольник, квадрат, круг и т.д.
Поскольку это просто общая «форма», метод вычисления площади area() будет возвращать ноль.
Чтобы узнать площадь конкретной фигуры, нужно создать подкласс, унаследованный от класса Shape, и в нем переопределить метод area() .
От класса Shape наследуется класс Circle, который тоже представляет собой форму.
Метод area() базового класса наследуется классом Circle и становится доступен в нем, но нам нужно переопределить метод area() в классе Circle, таким образом, чтобы он вычислял площадь круга.
Преимущество использования наследования в том, что вы можете написать код, который можно применить к ряду классов, расширяющих более общий класс.
Создадим класс Main, и в нем напишем метод, который вычисляет большую площадь двух фигур:
Как вы можете видеть, метод getLargerShape() не требует указания определенного типа фигуры для его двух параметров. В качестве параметров для этого метода можно использовать экземпляр любого класса, который наследует тип Shape. Можно использовать экземпляр класса круг, прямоугольник, треугольник, трапеция, и т.д. – до тех пор, как они наследуют класс формы.
Резюме:
Наследование классов ( inheritance ) один из существенных атрибутов ООП (объектно-ориентированного программирования). Оно позволяет строить новые классы на базе существующих, добавляя в них новые возможности или переопределяя существующие.
Унаследованные поля могут быть использованы непосредственно, как и любые другие поля. Вы можете объявить поле в дочернем классе с тем же именем, что и в суперклассе, скрывая его таким образом (но это не рекомендуется). Вы можете объявить новые поля в подклассе, которых нет в суперклассе. Унаследованные методы тоже можно использовать непосредственно. Вы можете написать в подклассе новый метод, который имеет ту же сигнатуру, что и метод суперкласса, это называется переопределением метода. Вы можете написать новый статический метод в подклассе, который имеет ту же сигнатуру, что и метод суперкласса, таким образом, скрывая его (то есть метод суперкласа будет виден внутри подкласса только через ключевое слово super). Вы можете объявить новые методы в подклассе, которых нет в суперклассе. Вы можете написать конструктор подкласса, который вызывает конструктор суперкласса, неявно или с помощью ключевого слова super. Подкласс не наследует закрытые члены родительского класса, например, обозначенные модификатором private.