Почему в Java нет множественного наследования, а реализация нескольких интерфейсов разрешена?
Java не допускает множественного наследования, но позволяет реализовать несколько интерфейсов. Зачем?
21 ответ
Поскольку интерфейсы определяют только то, что делает класс, а не как он это делает.
Проблема множественного наследования заключается в том, что два класса могут определять разные способы выполнения одной и той же вещи, и подкласс не может выбирать, какой из них выбрать.
Один из моих преподавателей в колледже объяснил мне это так:
Предположим, у меня есть один класс, который является тостером, и другой класс, который является ядерной бомбой. Они оба могут иметь настройку "тьмы". У них обоих есть метод on(). (У одного есть off(), у другого нет.) Если я хочу создать класс, который является подклассом обоих из них... как вы можете видеть, это проблема, которая может действительно взорваться мне в лицо,
Итак, одна из основных проблем заключается в том, что если у вас есть два родительских класса, они могут иметь разные реализации одной и той же функции - или, возможно, две разные функции с одинаковым именем, как в примере моего инструктора. Затем вы должны решить, какой из ваших подклассов использовать. Конечно, есть способы справиться с этим - C++ делает это - но разработчики Java чувствовали, что это усложнит ситуацию.
Тем не менее, с помощью интерфейса вы описываете то, на что способен класс, а не заимствуете метод другого класса. Несколько интерфейсов с гораздо меньшей вероятностью могут вызвать сложные конфликты, которые необходимо разрешить, чем несколько родительских классов.
Поскольку наследование используется слишком часто, даже если вы не можете сказать "эй, этот метод выглядит полезным, я также расширю этот класс".
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
Ответ на этот вопрос заключается во внутренней работе компилятора Java (цепочки конструктора). Если мы видим внутреннюю работу компилятора Java:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI {
public void printBankBalance(){
System.out.println("20k");
}
}
После компиляции это выглядит так:
public class Bank {
public Bank(){
super();
}
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank {
SBI(){
super();
}
public void printBankBalance(){
System.out.println("20k");
}
}
когда мы расширяем класс и создаем его объект, одна цепочка конструктора будет работать до Object
учебный класс.
Выше код будет работать нормально. но если у нас есть другой класс под названием Car
который расширяется Bank
и один гибридный(множественное наследование) класс называется SBICar
:
class Car extends Bank {
Car() {
super();
}
public void run(){
System.out.println("99Km/h");
}
}
class SBICar extends Bank, Car {
SBICar() {
super(); //NOTE: compile time ambiguity.
}
public void run() {
System.out.println("99Km/h");
}
public void printBankBalance(){
System.out.println("20k");
}
}
В этом случае (SBICar) не удастся создать цепочку конструктора (неоднозначность времени компиляции).
Для интерфейсов это разрешено, потому что мы не можем создать его объект.
За новую концепцию default
а также static
Метод, пожалуйста, обратитесь по умолчанию в интерфейсе.
Надеюсь, что это решит ваш запрос. Благодарю.
Вы можете найти точный ответ на этот запрос на странице документации оракула о множественном наследовании.
Многократное наследование состояния: возможность наследовать поля от нескольких классов
Одна из причин, по которой язык программирования Java не позволяет расширять более одного класса, состоит в том, чтобы избежать проблем множественного наследования состояний, а именно способности наследовать поля от нескольких классов.
Если допускается множественное наследование, и когда вы создаете объект путем создания экземпляра этого класса, этот объект будет наследовать поля от всех суперклассов класса. Это вызовет две проблемы.
- Что если методы или конструкторы из разных суперклассов создают одно и то же поле?
- Какой метод или конструктор будет иметь приоритет?
Многократное наследование реализации: возможность наследовать определения методов от нескольких классов.
Проблемы с этим подходом: назовите конфликт и неопределенность. Если подкласс и суперкласс содержат одно и то же имя метода (и сигнатуру), компилятор не может определить, какую версию вызывать.
Но java поддерживает этот тип множественного наследования с помощью методов по умолчанию, которые появились с версии Java 8. Компилятор Java предоставляет некоторые правила, чтобы определить, какой метод по умолчанию использует конкретный класс.
Для получения более подробной информации по решению проблемы алмазов см. Ниже.
Каковы различия между абстрактными классами и интерфейсами в Java 8?
Множественное наследование типа: способность класса реализовать более одного интерфейса.
Так как интерфейс не содержит изменяемых полей, вам не нужно беспокоиться о проблемах, возникающих в результате множественного наследования состояний.
Java не поддерживает множественное наследование по двум причинам:
- В java каждый класс является потомком
Object
учебный класс. Когда он наследуется от более чем одного суперкласса, подкласс получает неоднозначность, чтобы получить свойство класса Object. - В java у каждого класса есть конструктор, напишем мы его явно или не напишем вообще. Первое заявление вызывает
super()
для вызова конструктора класса ужина. Если у класса более одного суперкласса, он запутается.
Поэтому, когда один класс расширяется от более чем одного суперкласса, мы получаем ошибку времени компиляции.
Реализация нескольких интерфейсов очень полезна и не доставляет особых проблем ни языковым, ни программистам. Так что это разрешено. Многократное наследование, хотя и полезно, может вызвать серьезные проблемы для пользователей (страшный алмаз смерти). И большинство вещей, которые вы делаете с множественным наследованием, также может быть выполнено с помощью композиции или с использованием внутренних классов. Поэтому множественное наследование запрещено, поскольку оно приносит больше проблем, чем выгод.
Java поддерживает множественное наследование только через интерфейсы. Класс может реализовывать любое количество интерфейсов, но может расширять только один класс.
Множественное наследование не поддерживается, потому что это приводит к смертельной проблеме с бриллиантами. Тем не менее, это может быть решено, но это приводит к сложной системе, поэтому основатели Java отбросили множественное наследование.
В официальном документе Джеймса Гослинга под названием "Java: обзор" в феврале 1995 года ( ссылка) дается представление о том, почему множественное наследование не поддерживается в Java.
По словам Гослинга:
"JAVA пропускает многие редко используемые, плохо понимаемые, запутывающие возможности C++, которые, по нашему опыту, приносят больше горя, чем пользы. Это в первую очередь состоит из перегрузки операторов (хотя в ней есть перегрузка методов), множественного наследования и обширных автоматических приведений".
Говорят, что состояние объектов указывается относительно полей в нем, и оно стало бы неоднозначным, если бы слишком много классов было унаследовано. Вот ссылка
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
Поскольку эта тема не близка, я опубликую этот ответ, я надеюсь, что это поможет кому-то понять, почему java не допускает множественное наследование.
Рассмотрим следующий класс:
public class Abc{
public void doSomething(){
}
}
В этом случае класс Abc не расширяет ничего, верно? Не так быстро, этот класс неявно расширяет класс Object, базовый класс, который позволяет всем работать в Java. Все является объектом.
Если вы попытаетесь использовать приведенный выше класс, вы увидите, что ваша IDE позволяет вам использовать такие методы, как: equals(Object o)
, toString()
и т. д., но вы не объявили эти методы, они пришли из базового класса Object
Вы можете попробовать:
public class Abc extends String{
public void doSomething(){
}
}
Это нормально, потому что ваш класс не будет неявно расширяться Object
но будет продолжаться String
потому что ты это сказал. Рассмотрим следующее изменение:
public class Abc{
public void doSomething(){
}
@Override
public String toString(){
return "hello";
}
}
Теперь ваш класс всегда будет возвращать "привет", если вы вызываете toString().
Теперь представьте следующий класс:
public class Flyer{
public void makeFly(){
}
}
public class Bird extends Abc, Flyer{
public void doAnotherThing(){
}
}
Опять класс Flyer
неявный расширяет объект, который имеет метод toString()
любой класс будет иметь этот метод, так как они все расширяются Object
косвенно, так что если вы позвоните toString()
от Bird
, который toString()
Ява будет использовать? От Abc
или же Flyer
? Это произойдет с любым классом, который пытается расширить два или более классов, чтобы избежать такого "столкновения методов", в котором они построили идею интерфейса, в основном вы могли бы думать о них как об абстрактном классе, который не расширяет Object косвенно. Поскольку они являются абстрактными, они должны быть реализованы классом, который является объектом (вы не можете создать экземпляр интерфейса самостоятельно, они должны быть реализованы классом), поэтому все будет работать нормально.
Чтобы отличать классы от интерфейсов, ключевое слово Implements было зарезервировано только для интерфейсов.
Вы можете реализовать любой интерфейс, который вам нравится, в одном и том же классе, так как он по умолчанию ничего не расширяет (но вы можете создать интерфейс, который расширяет другой интерфейс, но, опять же, интерфейс "папа" не будет расширять объект "), поэтому интерфейс просто интерфейс, и они не пострадают от "коллизий сигнатур методов", если они это сделают, компилятор выдаст вам предупреждение, и вам просто нужно изменить сигнатуру метода, чтобы исправить ее (сигнатура = имя метода + параметры + тип возврата),
public interface Flyer{
public void makeFly(); // <- method without implementation
}
public class Bird extends Abc implements Flyer{
public void doAnotherThing(){
}
@Override
public void makeFly(){ // <- implementation of Flyer interface
}
// Flyer does not have toString() method or any method from class Object,
// no method signature collision will happen here
}
По той же причине C# не допускает множественного наследования, но позволяет реализовать несколько интерфейсов.
Урок, извлеченный из C++ с множественным наследованием, заключался в том, что это привело к большему количеству проблем, чем оно того стоило.
Интерфейс - это контракт вещей, которые должен реализовать ваш класс. Вы не получаете никакой функциональности от интерфейса. Наследование позволяет вам наследовать функциональность родительского класса (и в случае множественного наследования это может привести к путанице).
Разрешение нескольких интерфейсов позволяет вам использовать шаблоны проектирования (например, адаптер) для решения тех же типов проблем, которые вы можете решить, используя множественное наследование, но гораздо более надежным и предсказуемым образом.
Например, два класса A, B, имеющие один и тот же метод m1(). И класс C расширяет как A, B.
class C extends A, B // for explaining purpose.
Теперь класс C будет искать определение m1. Во-первых, он будет искать в классе, если он не нашел, тогда он будет проверять в классе родителей. Оба A, B имеют определение. Итак, здесь возникает неопределенность, какое определение следует выбрать. Так что JAVA не поддерживает множественное наследование.
Множественное наследование не поддерживается классом из-за неоднозначности. (этот момент ясно объяснен в приведенных выше ответах с использованием ключевого слова super)
Теперь об интерфейсах
до Java 7 интерфейсы не могли определять реализацию методов. Поэтому, если класс реализуется из нескольких интерфейсов, имеющих одинаковую сигнатуру метода, тогда реализация этого метода должна быть предоставлена этим классом.
начиная с java 8 интерфейсы также могут иметь реализацию методов. Поэтому, если класс реализует два или более интерфейса, имеющих одинаковую сигнатуру метода с реализацией, то он также обязан реализовать метод в этом классе.
Начиная с Java 9, интерфейсы могут содержать статические методы, частные методы, частные статические методы.
Изменения в функциях интерфейсов (по сравнению с версиями java-7,8,9)
Все мы знаем, что мы можем наследовать (расширять) один класс, но мы можем реализовать так много интерфейсов... это потому, что в интерфейсах мы не даем реализацию, просто говорим о функциональности. Предположу, если Java может, распространяется так много классов и те же методы, есть.. в этот момент, если мы попытаемся вызвать метод суперкласса в классе юга, какой метод предполагает, чтобы запустить??, компилятору запутаться пример - попытка кратному продолжается, но в интерфейсы, у этих методов нет тел, мы должны реализовать их в подклассе.. попробуйте несколько инструментов, так что не беспокойтесь..
Потому что интерфейс - это просто контракт. А класс на самом деле является контейнером для данных.
Рассмотрим сценарий, в котором Test1, Test2 и Test3 представляют собой три класса. Класс Test3 наследует классы Test2 и Test1. Если классы Test1 и Test2 имеют один и тот же метод, и вы вызываете его из дочернего объекта класса, будет неоднозначность для вызова метода класса Test1 или Test2, но нет такой неоднозначности для интерфейса, поскольку в интерфейсе нет реализации.
Java не поддерживает множественное наследование, многолучевое и гибридное наследование из-за проблемы неоднозначности:
Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ? So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.
Ссылочная ссылка: https://plus.google.com/u/0/communities/102217496457095083679
Возьмем, к примеру, случай, когда у класса A есть метод getSomething, а у класса B есть метод getSomething, а класс C расширяет A и B. Что бы произошло, если бы кто-то вызвал C.getSomething? Нет способа определить, какой метод вызывать.
Интерфейсы в основном просто указывают, какие методы должен содержать реализующий класс. Класс, который реализует несколько интерфейсов, просто означает, что класс должен реализовывать методы из всех этих интерфейсов. Это не приведет к каким-либо проблемам, описанным выше.
Это решение сохранить низкий уровень сложности. С гибридным наследованием все было бы сложнее реализовать, и в любом случае то, что достижимо с помощью множественного наследования, возможно и другими способами.
изображение, объясняющее проблему с множественным наследованием. Просто посмотрите изображение выше
что такое унаследованный член производного класса? он по-прежнему является частным или общедоступным в производном классе? чтобы избежать проблем такого типа в java, они удаляют множественное наследование. это изображение - простой пример задачи объектно-ориентированного программирования.
* Это простой ответ, так как я новичок в Java *
Считайте, что есть три класса X
,Y
а также Z
,
Таким образом, мы наследуем как X extends Y, Z
И оба Y
а также Z
имеет метод alphabet()
с тем же типом возврата и аргументами. Этот метод alphabet()
в Y
говорит отображать первый алфавит и метод алфавит в Z
говорит отображать последний алфавит. Так что здесь возникает двусмысленность, когда alphabet()
называется X
, Говорит ли он отображать первый или последний алфавит??? Таким образом, Java не поддерживает множественное наследование. В случае интерфейсов рассмотрим Y
а также Z
в качестве интерфейсов. Таким образом, оба будут содержать объявление метода alphabet()
но не определение. Он не скажет, отображать ли первый алфавит или последний алфавит или что-то еще, но просто объявит метод alphabet()
, Так что нет причин поднимать неопределенность. Мы можем определить метод с чем угодно внутри класса X
,
Таким образом, одним словом, в Интерфейсах определение делается после реализации, поэтому нет путаницы.