Почему в Java нет множественного наследования, но разрешена реализация нескольких интерфейсов? - PullRequest
140 голосов
/ 25 марта 2010

Java не допускает множественное наследование, но позволяет реализовать несколько интерфейсов. Почему?

Ответы [ 16 ]

204 голосов
/ 25 марта 2010

Поскольку интерфейсы указывают только , что делает класс, а не , как это делает.

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

89 голосов
/ 25 марта 2010

Один из моих преподавателей в колледже объяснил мне это так:

Предположим, у меня есть один класс, который является тостером, и другой класс, который является ядерной бомбой. У них обоих может быть «темнота». У них обоих есть метод on (). (У одного есть off (), у другого нет.) Если я хочу создать класс, который является подклассом обоих из них ... как вы можете видеть, это проблема, которая может действительно взорваться мне в лицо .

Итак, одна из основных проблем заключается в том, что если у вас есть два родительских класса, они могут иметь разные реализации одной и той же функции или, возможно, две разные функции с одинаковым именем, как в примере моего инструктора. Затем вы должны решить, какой из ваших подклассов использовать. Конечно, есть способы справиться с этим - C ++ делает это - но разработчики Java чувствовали, что это усложнит ситуацию.

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

22 голосов
/ 25 марта 2010

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

public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter, 
             AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
10 голосов
/ 25 марта 2019

Ответ на этот вопрос заключается во внутренней работе компилятора 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, пожалуйста, обратитесь к default в интерфейсе .

Надеюсь, что это решит ваш запрос. Спасибо.

7 голосов
/ 25 марта 2010

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

4 голосов
/ 28 сентября 2016

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

Множественное наследование не поддерживается, поскольку оно приводит к смертельной проблеме с алмазом. Тем не менее, это может быть решено, но это приводит к сложной системе, поэтому множественное наследование было отброшено основателями Java.

В белой книге Джеймса Гослинга под названием «Java: обзор» в феврале 1995 года ( ссылка ) дается представление о том, почему множественное наследование не поддерживается в Java.

По Гослингу:

"JAVA опускает многие редко используемые, плохо понятые, запутанные особенности C ++, который по нашему опыту приносит больше горя, чем пользы. это в основном состоит из перегрузки операторов (хотя перегрузка метода), множественное наследование и расширенный автоматический Приведение ".

3 голосов
/ 17 сентября 2016

Вы можете найти точный ответ на этот запрос на странице документации оракула о множественном наследовании

  1. Множественное наследование состояния: Возможность наследовать поля от нескольких классов

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

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

    1. Что если методы или конструкторы из разных суперклассов создают одно и то же поле?
    2. Какой метод или конструктор будет иметь приоритет?
  2. Множественное наследование реализации: Возможность наследовать определения методов от нескольких классов

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

    Но java поддерживает этот тип множественного наследования с методами по умолчанию , которые появились с версии Java 8. Компилятор Java предоставляет некоторые правила для определения того, какой метод по умолчанию используется конкретным классом.

    См. Ниже сообщение SE для получения более подробной информации о решении проблемы алмазов:

    В чем различия между абстрактными классами и интерфейсами в Java 8?

  3. Множественное наследование типа: Возможность класса реализовать более одного интерфейса.

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

3 голосов
/ 25 марта 2010

По той же причине C # не допускает множественное наследование, но позволяет реализовать несколько интерфейсов.

Урок, извлеченный из C ++ с множественным наследованием, заключался в том, что это привело к большему количеству проблем, чем оно того стоило.1003 *

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

Разрешение нескольких интерфейсов позволяет использовать шаблоны проектирования (например, Adapter) для решения одних и тех же типовпроблемы, которые вы можете решить, используя множественное наследование, но гораздо более надежным и предсказуемым образом.

2 голосов
/ 11 июня 2016

Поскольку эта тема не близка, я опубликую этот ответ, я надеюсь, что это поможет кому-то понять, почему 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 неявно расширяет Object, у которого есть метод toString(), у любого класса будет этот метод, поскольку все они расширяются Object косвенно, поэтому, если вы вызываете toString() из Bird, что toString() Java должен был бы использовать? От Abc или Flyer? Это произойдет с любым классом, который пытается расширить два или более классов, чтобы избежать такого «коллизии методов», они создали идею interface , в основном вы могли бы думать о них как о абстрактном классе, который не расширяет объект косвенно . Поскольку они абстрактные , они должны быть реализованы классом, который является объектом (вы не можете создать экземпляр интерфейса самостоятельно, они должны быть реализованы классом), поэтому все будет продолжайте работать нормально.

Чтобы отличать классы от интерфейсов, ключевое слово реализует зарезервировано только для интерфейсов.

Вы можете реализовать любой интерфейс, который вам нравится, в одном и том же классе, поскольку по умолчанию они ничего не расширяют (но вы можете создать интерфейс, который расширяет другой интерфейс, но, опять же, интерфейс «папа» не расширяет Object »), поэтому Интерфейс - это просто интерфейс, и они не пострадают от " colissions сигнатур методов ", если они это сделают, компилятор выдаст вам предупреждение, и вам просто нужно изменить сигнатуру метода, чтобы исправить это (подпись = имя метода + params + тип возвращаемого значения).

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

}
2 голосов
/ 13 февраля 2014

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

http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html

...