Java не допускает множественного наследования, но позволяет реализовать несколько интерфейсов. Почему?
Почему в Java нет множественного наследования, но разрешена реализация нескольких интерфейсов?
Ответы (19)
Поскольку интерфейсы указывают только что делает класс, а не как он это делает.
Проблема с множественным наследованием заключается в том, что два класса могут определять разные способы выполнения одного и того же действия, и подкласс не может выбрать, какой из них выбрать.
Deadly Diamond of Death
никогда не было проблемой, когда дело касалось методов. Важно столкновение переменных. Потому что с помощью методов по умолчанию в интерфейсах у вас все еще может быть свое ромбовидное созвездие, но вы не можете столкнуться с переменными. Java 8 доказывает, что это неверно. Ответы на этот вопрос следует переустановить, и на них следует отвечать с учетом последних достижений Java.
- person JayC667; 03.03.2020
Один из моих преподавателей в колледже объяснил мне это так:
Предположим, у меня есть один класс - тостер, а другой - NuclearBomb. У них обоих может быть настройка «тьма». У них обоих есть метод 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 extends Bank{
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
, обратитесь к по умолчанию в интерфейсе а>.
Надеюсь, это решит ваш вопрос. Спасибо.
Вы можете найти точный ответ на этот запрос на странице документации Oracle о множественном наследовании а>
Множественное наследование состояния: возможность наследовать поля от нескольких классов.
Одна из причин, по которой язык программирования Java не позволяет вам расширять более одного класса, заключается в том, чтобы избежать проблем множественного наследования состояния, то есть возможности наследовать поля от нескольких классов.
Если разрешено множественное наследование и когда вы создаете объект путем создания экземпляра этого класса, этот объект будет наследовать поля от всех суперклассов класса. Это вызовет две проблемы.
- What if methods or constructors from different super classes instantiate the same field?
- Какой метод или конструктор будет иметь приоритет?
Множественное наследование реализации: возможность наследовать определения методов от нескольких классов.
Проблемы с этим подходом: конфликт имен и двусмысленность. Если подкласс и суперкласс содержат одно и то же имя метода (и подпись), компилятор не может определить, какую версию вызывать.
Но Java поддерживает этот тип множественного наследования с помощью методов по умолчанию, которые были введены с момента выпуска Java 8. Компилятор Java предоставляет некоторые правила для определения того, какой метод по умолчанию использует конкретный класс.
Обратитесь к нижеприведенному сообщению SE для получения более подробной информации о решении проблемы с бриллиантами:
В чем различия между абстрактными классами и интерфейсами в Java 8?
Множественное наследование типа: способность класса реализовывать более одного интерфейса.
Поскольку интерфейс не содержит изменяемых полей, вам не нужно беспокоиться о проблемах, возникающих в результате множественного наследования состояния.
Реализация нескольких интерфейсов очень полезна и не вызывает особых проблем ни у разработчиков языка, ни у программистов. Так что это разрешено. Множественное наследование, хотя и полезно, может вызвать серьезные проблемы для пользователей (страшный алмаз смерти). И большинство вещей, которые вы делаете с множественным наследованием, также можно сделать с помощью композиции или использования внутренних классов. Таким образом, множественное наследование запрещено, поскольку оно приносит больше проблем, чем приносит пользу.
ToyotaCar
и HybridCar
оба являются производными от Car
и переопределяют Car.Drive
, и если PriusCar
унаследовал оба , но не переопределяет Drive
, система не сможет определить, что виртуальный Car.Drive
должен делать. Интерфейсы избегают этой проблемы, избегая выделенного курсивом условия выше.
- person supercat; 16.06.2013
Drive
или преобразовать его в Car
без предварительного преобразования в HybridCar
или ToyotaCar
, двусмысленности можно было бы избежать, но результат последовательности преобразования _5 _- ›_ 6 _-› _ 7_ будет отличаться от результата PriusCar
- ›_ 9 _-› _ 10_ (это означает, что приведение не будет сохранять идентичность).
- person supercat; 16.06.2013
void UseCar(Car &foo)
; нельзя ожидать, что он будет включать устранение неоднозначности между ToyotaCar::Drive
и HybridCar::Drive
(поскольку он часто не должен знать и не заботиться о том, что эти другие типы даже существуют). Язык может, как и C ++, требовать, чтобы этот код ToyotaCar &myCar
, желающий передать его UseCar
, должен сначала привести к HybridCar
или ToyotaCar
, но поскольку ((Car) (HybridCar) myCar) .Drive` и ((Car)(ToyotaCar)myCar).Drive
будут делать разные вещи, это будет означать, что апкасты не сохраняли идентичность.
- person supercat; 13.08.2015
Java поддерживает множественное наследование только через интерфейсы. Класс может реализовывать любое количество интерфейсов, но может расширять только один класс.
Множественное наследование не поддерживается, потому что это приводит к смертельной проблеме с бриллиантами. Однако это можно решить, но это приводит к сложной системе, поэтому основатели Java отказались от множественного наследования.
В официальном документе под названием «Java: обзор», написанном Джеймсом Гослингом в феврале 1995 г. (ссылка - стр. 2) дает представление о том, почему множественное наследование не поддерживается в Java.
По словам Гослинга:
В JAVA отсутствуют многие редко используемые, плохо понимаемые, сбивающие с толку функции C ++, которые, по нашему опыту, приносят больше горя, чем пользы. Это в первую очередь состоит из перегрузки оператора (хотя и имеет перегрузку метода), множественного наследования и обширного автоматического приведения.
Говорят, что состояние объектов указывается в соответствии с полями в нем, и это станет неоднозначным, если будет унаследовано слишком много классов. Вот ссылка
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
По той же причине C # не допускает множественного наследования, но позволяет реализовать несколько интерфейсов.
Урок, извлеченный из C ++ с множественным наследованием, заключался в том, что это приводит к большему количеству проблем, чем оно того стоит.
Интерфейс - это контракт вещей, который должен реализовать ваш класс. Вы не получаете никакой функциональности от интерфейса. Наследование позволяет вам наследовать функциональность родительского класса (а при множественном наследовании это может сильно запутать).
Разрешение нескольких интерфейсов позволяет вам использовать шаблоны проектирования (например, адаптер) для решения тех же типов проблем, которые вы можете решить с помощью множественного наследования, но гораздо более надежным и предсказуемым образом.
D1
и D2
оба наследуются от B
, и каждая переопределяет функцию f
, и если obj
является экземпляром типа S
, который наследуется от D1
и D2
, но не отменяет f
, то приведение ссылки на S
к D1
должно что-то дать f
которого использует переопределение D1
, и приведение к B
не должно это менять. Точно так же приведение ссылки S
к D2
должно привести к чему-то, f
которого использует D2
переопределение, а приведение к B
не должно этого изменить. Если для языка не нужно было разрешать добавление виртуальных участников ...
- person supercat; 27.09.2015
D1
и D2
можно изменить и перекомпилировать без перекомпиляции S
, тогда могут возникнуть неоднозначности, которые не сможет сделать ни один компилятор чтобы увидеть.
- person supercat; 27.09.2015
D1
, и D2
(или даже один из них) переопределяют f
, то S
также должен переопределить его, иначе это ошибка компилятора (поскольку при вызове f
объекта объявленного типа S
, как можно решить звонить ли D1::f
или D2::f
?)
- person The Paramagnetic Croissant; 27.09.2015
S
компилируется в то время, когда ни D1
, ни D2
не переопределяют f
, но оба класса будут изменены в будущем, чтобы переопределить его, на каком этапе компиляции должен произойти крик? Компиляция S
не может кричать, потому что не знает, что D1
и D2
изменятся. Сборники D1
и D2
не могут кричать, так как ничего не знают о S
.
- person supercat; 27.09.2015
Поскольку эта тема не закрыта, я отправлю этот ответ, надеюсь, это поможет кому-то понять, почему 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()
java должна будет использовать? С Abc
или Flyer
? Это произойдет с любым классом, который пытается расширить два или более классов, чтобы избежать такого «столкновения методов», они построили идею интерфейса, в основном вы могли бы думать их как абстрактный класс который не расширяет Object косвенно. Поскольку они абстрактны, они должны быть реализованы классом, который является объектом (вы не можете создать экземпляр интерфейса в одиночку, они должны быть реализованы классом), поэтому все будет работать нормально.
Чтобы отличать классы от интерфейсов, ключевое слово реализует только для интерфейсов.
Вы можете реализовать любой интерфейс, который вам нравится, в том же классе, поскольку они по умолчанию ничего не расширяют (но вы можете создать интерфейс, который расширяет другой интерфейс, но, опять же, «родительский» интерфейс не расширяет Object »), поэтому интерфейс просто интерфейс, и они не будут страдать от "коллизий сигнатур методов", если они это сделают, компилятор выдаст вам предупреждение, и вам просто нужно будет изменить сигнатуру метода, чтобы исправить это (подпись = метод имя + параметры + возвращаемый тип).
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
}
Java не поддерживает множественное наследование по двум причинам:
- В java каждый класс является дочерним по отношению к классу
Object
. Когда он наследуется от более чем одного суперкласса, подкласс получает неоднозначность, чтобы получить свойство класса Object. - В java у каждого класса есть конструктор, напишем мы его явно или не напишем вообще. Первый оператор вызывает
super()
для вызова конструктора класса ужина. Если у класса более одного суперкласса, он запутается.
Поэтому, когда один класс расширяется от нескольких суперклассов, мы получаем ошибку времени компиляции.
Потому что интерфейс - это просто контракт. А класс на самом деле является контейнером для данных.
Например, два класса A, B имеют один и тот же метод m1 (). А класс C расширяет как A, так и B.
class C extends A, B // for explaining purpose.
Теперь класс C будет искать определение m1. Сначала он будет искать в классе, если не нашел, затем проверит класс родителей. Оба A и B имеют определение. Итак, здесь возникает двусмысленность, какое определение следует выбрать. Таким образом, JAVA НЕ ПОДДЕРЖИВАЕТ МНОЖЕСТВЕННОЕ НАСЛЕДОВАНИЕ.
Все мы знаем, что мы можем наследовать (расширять) один класс, но мы можем реализовать так много интерфейсов ... это потому, что в интерфейсах мы не даем реализации, просто говорим о функциональности. предположим, если java может расширять так много классов и у них одинаковые методы ... в этом месте, если мы попытаемся вызвать метод суперкласса в подклассе, какой метод предполагается запустить ??, компилятор запутается пример: - попробуйте несколько расширений, но в интерфейсах эти методы не имеют тел, которые мы должны реализовать в подклассе .. попробуйте использовать несколько орудий, так что не беспокойтесь ..
Возьмем, к примеру, случай, когда класс A имеет метод getSomething, а класс B имеет метод getSomething, а класс C расширяет A и B. Что произойдет, если кто-то вызовет C.getSomething? Невозможно определить, какой метод вызывать.
Интерфейсы в основном просто указывают, какие методы должен содержать реализующий класс. Класс, реализующий несколько интерфейсов, просто означает, что класс должен реализовать методы из всех этих интерфейсов. Whci не приведет к каким-либо проблемам, описанным выше.
Рассмотрим сценарий, в котором 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
* Это простой ответ, поскольку я новичок в Java *
Представьте, что существует три класса _1 _, _ 2_ и Z
.
Таким образом, мы наследуем как X extends Y, Z
И как Y
, так и Z
имеют метод alphabet()
с тем же типом возвращаемого значения и аргументами. Этот метод alphabet()
в Y
указывает отображать первый алфавит, а алфавит метода в Z
говорит отображать последний алфавит. Таким образом, возникает двусмысленность, когда alphabet()
вызывается X
. Говорит ли он отображать первый или последний алфавит ??? Итак, java не поддерживает множественное наследование. В случае интерфейсов считайте Y
и Z
интерфейсами. Таким образом, оба будут содержать объявление метода alphabet()
, но не определение. Он не скажет, отображать ли первый алфавит или последний алфавит или что-то еще, а просто объявит метод alphabet()
. Так что нет причин вызывать двусмысленность. Мы можем определить метод с чем угодно внутри класса X
.
Одним словом, определение интерфейсов выполняется после реализации, поэтому нет путаницы.
Это решение сохранить низкую сложность. С гибридным наследованием все было бы сложнее реализовать, и в любом случае то, что можно достичь с помощью множественного наследования, можно сделать и другими способами.