Java не допускает множественное наследование, но позволяет реализовать несколько интерфейсов. Зачем?
Java не допускает множественное наследование, но позволяет реализовать несколько интерфейсов. Зачем?
Ответы:
Поскольку интерфейсы определяют только то, что делает класс, а не как он это делает.
Проблема множественного наследования заключается в том, что два класса могут определять разные способы выполнения одной и той же вещи, и подкласс не может выбирать, какой из них выбрать.
Один из преподавателей моего колледжа объяснил мне это так:
Предположим, у меня есть один класс, который является Toaster, и другой класс, который является 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
методом обращайтесь по умолчанию в интерфейс .
Надеюсь, что это решит ваш запрос. Спасибо.
Реализация нескольких интерфейсов очень полезна и не доставляет особых проблем ни языковым, ни программистам. Так что это разрешено. Многократное наследование, хотя и полезно, может вызвать серьезные проблемы для пользователей (страшный алмаз смерти ). И большинство вещей, которые вы делаете с множественным наследованием, также может быть выполнено с помощью композиции или с использованием внутренних классов. Поэтому множественное наследование запрещено, поскольку оно приносит больше проблем, чем выгод.
ToyotaCar
и HybridCar
оба получены Car
и переопределеныCar.Drive
, и если PriusCar
унаследованы оба, но не переопределеныDrive
, система не сможет определить, что Car.Drive
должен делать виртуал. Интерфейсы избегают этой проблемы, избегая выделенного курсивом условия выше.
void UseCar(Car &foo)
; Нельзя ожидать, что они будут содержать неоднозначность между ToyotaCar::Drive
и HybridCar::Drive
(поскольку часто не нужно ни знать, ни заботиться о том, чтобы эти другие типы вообще существовали ). Язык может, как это делает C ++, требовать, чтобы код с ToyotaCar &myCar
желанием передать его UseCar
должен сначала привести к либо, HybridCar
либо ToyotaCar
, но, поскольку ((Car) (HybridCar) myCar) .Drive` и ((Car)(ToyotaCar)myCar).Drive
будет делать разные вещи, это будет означать, что upcasts не были сохранением идентичности.
Вы можете найти точный ответ на этот запрос на странице документации оракула о множественном наследовании.
Множественное наследование состояний: возможность наследовать поля от нескольких классов
Одна из причин, по которой язык программирования Java не позволяет расширять более одного класса, состоит в том, чтобы избежать проблем множественного наследования состояний, а именно способности наследовать поля от нескольких классов.
Если допускается множественное наследование, и когда вы создаете объект путем создания экземпляра этого класса, этот объект будет наследовать поля от всех суперклассов класса. Это вызовет две проблемы.
Множественное наследование реализации: Возможность наследовать определения методов от нескольких классов.
Проблемы с этим подходом: конфликтует имя и неопределенность . Если подкласс и суперкласс содержат одно и то же имя метода (и сигнатуру), компилятор не может определить, какую версию вызывать.
Но java поддерживает этот тип множественного наследования с методами по умолчанию , которые были введены после выпуска Java 8. Компилятор Java предоставляет некоторые правила, чтобы определить, какой метод по умолчанию использует конкретный класс.
Для получения более подробной информации о решении проблемы алмазов см. Ниже.
Каковы различия между абстрактными классами и интерфейсами в Java 8?
Множественное наследование типа: способность класса реализовать более одного интерфейса.
Поскольку интерфейс не содержит изменяемых полей, вам не нужно беспокоиться о проблемах, которые возникают в результате множественного наследования состояний.
Говорят, что состояние объектов указывается относительно полей в нем, и оно стало бы неоднозначным, если бы слишком много классов было унаследовано. Вот ссылка
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
Java поддерживает множественное наследование только через интерфейсы. Класс может реализовывать любое количество интерфейсов, но может расширять только один класс.
Многократное наследование не поддерживается, потому что это приводит к смертельной проблеме с алмазом. Тем не менее, это может быть решено, но это приводит к сложной системе, поэтому множественное наследование было отброшено основателями Java.
В официальном документе Джеймса Гослинга под названием «Java: обзор», опубликованном в феврале 1995 года ( ссылка ), дается представление о том, почему множественное наследование не поддерживается в Java.
По словам Гослинга:
«JAVA пропускает многие редко используемые, плохо понимаемые, запутывающие возможности C ++, которые, по нашему опыту, приносят больше горя, чем пользы. Это в первую очередь состоит из перегрузки операторов (хотя в ней есть перегрузка методов), множественного наследования и обширных автоматических приведений».
По той же причине C # не допускает множественного наследования, но позволяет реализовать несколько интерфейсов.
Урок, извлеченный из C ++ с множественным наследованием, заключался в том, что это привело к большему количеству проблем, чем оно того стоило.
Интерфейс - это контракт вещей, которые должен реализовать ваш класс. Вы не получаете никакой функциональности от интерфейса. Наследование позволяет вам наследовать функциональность родительского класса (и в случае множественного наследования это может привести к путанице).
Разрешение нескольких интерфейсов позволяет использовать шаблоны проектирования (например, адаптер) для решения тех же типов проблем, которые можно решить с помощью множественного наследования, но гораздо более надежным и предсказуемым образом.
D1
и D2
как наследовать от B
, и каждый переопределяет функцию f
, и если obj
это экземпляр типа , S
который наследует от обоих D1
и , D2
но не отменяет f
, а затем отливки ссылку на , S
чтобы D1
должно дать что - то , чье f
использует D1
Override и отливку , чтобы B
не должен изменить это. Аналогично, приведение ссылки S
на D2
должно привести к тому, что f
использует D2
переопределение, а приведение к B
не должно это изменить. Если языку не нужно разрешать добавление виртуальных членов ...
Поскольку эта тема не близка, я опубликую этот ответ, я надеюсь, что это поможет кому-то понять, почему 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
неявного extends Object, у которого есть метод toString()
, любой класс будет иметь этот метод, поскольку все они расширяются Object
косвенно, поэтому, если вы вызываете toString()
из Bird
, какую toString()
java придется использовать? От 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
}
Потому что интерфейс - это просто контракт. А класс на самом деле является контейнером для данных.
Например, два класса A, B, имеющие один и тот же метод m1 (). И класс C расширяет как A, B.
class C extends A, B // for explaining purpose.
Теперь класс C будет искать определение m1. Во-первых, он будет искать в классе, если он не нашел, тогда он будет проверять в классе родителей. Оба A, B имеют определение. Итак, здесь возникает неопределенность, какое определение следует выбрать. Так что JAVA НЕ ПОДДЕРЖИВАЕТ МНОГОКРАТНОЕ НАСЛЕДОВАНИЕ.
Java не поддерживает множественное наследование по двум причинам:
Object
класса. Когда он наследует от более чем одного суперкласса, подкласс получает неоднозначность для приобретения свойства класса Object.super()
для вызова конструктора класса ужин. Если в классе более одного суперкласса, он запутывается.Поэтому, когда один класс расширяется из нескольких суперклассов, мы получаем ошибку времени компиляции.
Возьмем, к примеру, случай, когда у класса A есть метод getSomething, а у класса B - метод getSomething, а класс C расширяет A и B. Что бы произошло, если бы кто-то вызвал C.getSomething? Нет способа определить, какой метод вызывать.
Интерфейсы в основном просто указывают, какие методы должен содержать реализующий класс. Класс, который реализует несколько интерфейсов, просто означает, что класс должен реализовывать методы из всех этих интерфейсов. Это не приведет к каким-либо проблемам, описанным выше.
Рассмотрим сценарий, в котором 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 может расширять так много классов и у них одни и те же методы. В этом случае, если мы попытаемся вызвать метод суперкласса в подклассе, какой метод предполагается запустить ??, компилятор запутается в примере: - попытаться использовать несколько расширений, но в интерфейсы, в которых эти методы не имеют тел, мы должны реализовать их в подклассе .. попробуйте несколько реализаций, так что не беспокойтесь ..
* Это простой ответ, так как я новичок в Java *
Считайте, что есть три класса X
, Y
и Z
.
Таким образом, мы наследуем как X extends Y, Z
And, так Y
и имеем Z
метод alphabet()
с тем же типом возврата и аргументами. Этот метод alphabet()
в Y
говорит отобразить первый алфавит и метод алфавит Z
говорит отображение последнего алфавит . Так что тут возникает двусмысленность, когда alphabet()
вызывается X
. Говорит ли он отображать первый или последний алфавит ??? Так что Java не поддерживает множественное наследование. В случае интерфейсов, рассмотрим Y
и Z
как интерфейсы. Таким образом, оба будут содержать объявление методаalphabet()
но не определение. Он не скажет, отображать ли первый или последний алфавит или что-то еще, но просто объявит методalphabet()
, Так что нет причин поднимать эту двусмысленность. Мы можем определить метод с чем угодно внутри класса X
.
Таким образом, одним словом, в Интерфейсах определение делается после реализации, поэтому нет путаницы.