Почему множественное наследование не разрешено в Java или C #?

Я знаю, что множественное наследование не разрешено в Java и C #. Многие книги просто говорят, что множественное наследование не допускается. Но это может быть реализовано с помощью интерфейсов. Ничего не говорится о том, почему это запрещено. Может ли кто-нибудь точно сказать, почему это запрещено?

Короткий ответ: потому что разработчики языка решили не делать этого.

В принципе, казалось, что и разработчики .NET, и Java не допускали множественного наследования, потому что они рассуждали о том, что добавление MI добавило слишком много сложностей для языков, при этом предоставляя слишком мало преимуществ .

Для более интересного и углубленного чтения есть несколько статей, доступных в Интернете с интервью с некоторыми из разработчиков языка. Например, для .NET Крис Брумме (который работал в MS на CLR) объяснил причины, по которым они решили не:

  1. У разных языков действительно есть разные ожидания относительно того, как работает МИ. Например, как разрешаются конфликты и дублируются ли дублирующие базы или дублируются. Прежде чем мы сможем внедрить MI в среде CLR, мы должны провести обзор всех языков, выяснить общие понятия и решить, как выразить их в нейтральной для языка форме. Нам также необходимо будет решить, принадлежит ли MI в CLS, и что это будет означать для языков, которые не хотят этого понятия (предположительно, VB.NET, например). Конечно, это тот бизнес, в котором мы находимся как общая среда исполнения, но мы пока не имеем возможности сделать это для MI.

  2. Число мест, где ИМ действительно подходит, на самом деле довольно мало. Во многих случаях наследование нескольких интерфейсов может выполнять работу вместо этого. В других случаях вы можете использовать инкапсуляцию и делегирование. Если бы мы добавили немного другую конструкцию, например mixins, это было бы действительно более мощным?

  3. Множественное наследование реализации усложняет реализацию. Эта сложность влияет на литье, компоновку, отправку, доступ к полям, сериализацию, сопоставление идентичности, проверяемость, reflection, генерики и, возможно, множество других мест.

Вы можете прочитать полную статью здесь.

Для Java вы можете прочитать эту статью :

Причины исключения множественного наследования с языка Java в основном связаны с «простой, объектно-ориентированной и знакомой» целью. Как простой язык, разработчики Java хотели, чтобы язык, который большинство разработчиков могли понять, без обширной тренировки. С этой целью они работали над тем, чтобы язык как можно более похожий на C ++ (знакомый), не перенося излишнюю сложность C ++ (простую).

По мнению дизайнеров, множественное наследование вызывает больше проблем и путаницы, чем решает. Таким образом, они сокращают множественное наследование от языка (так же, как они сокращают перегрузку оператора). Широкий опыт разработчиков C ++ показал, что множественное наследование просто не стоит головной боли.

Это недопустимо множественное наследование реализации .

Проблема в том, что компилятор / среда выполнения не могут понять, что делать, если у вас есть class Cowboy и Artist, как с реализациями для метода draw (), так и при попытке создать новый тип CowboyArtist. Что происходит, когда вы вызываете метод draw ()? Кто-то лежит мертвым на улице, или у вас прекрасная акварель?

Я считаю, что это называется проблемой двойного алмазного наследования.

Причина: Java очень популярен и легко кодируется из-за его простоты.

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

  1. Они избегали указателей
  2. Они избегали множественного наследования.

Проблема с множественным наследованием: проблема с алмазом.

Пример :

  1. Предположим, что class A имеет метод fun (). class B и class C происходит от classа A.
  2. И оба classа B и C переопределяют метод fun ().
  3. Теперь предположим, что class D наследует как class B, так и C. (просто предположение)
  4. Создать объект для classа D.
  5. D d = новый D ();
  6. и попытайтесь получить доступ к d.fun (); => будет ли это вызвано classом B fun () или fun () classа C?

Это двусмысленность, существующая в проблеме алмазов.

Невозможно решить эту проблему, но это создает больше путаницы и сложностей для программиста при его чтении. Это вызывает больше проблем, чем пытается решить.

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

Потому что Java имеет совершенно другую философию дизайна из C ++. (Я не собираюсь обсуждать здесь C #.)

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

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

Кроме того, Java была в значительной степени реакцией на C ++ и Smalltalk, наиболее известные языки OO. Существует множество других языков OO (Common Lisp на самом деле был первым стандартизованным), с различными OO-системами, которые лучше справляются с MI.

Не говоря уже о том, что вполне возможно сделать MI на Java, используя интерфейсы, состав и делегирование. Это более явное, чем в C ++, и поэтому неудобно использовать, но вы получите то, что вы, скорее всего, поймете на первый взгляд.

Здесь нет правильного ответа. Существуют разные ответы, которые лучше для конкретной ситуации зависят от приложений и индивидуальных предпочтений.

Основная (хотя и не единственная) причина, по которой люди уходят от МИ, – это так называемая «проблема с алмазами», приводящая к двусмысленности в вашей реализации. Эта статья в Википедии обсуждает это и объясняет лучше, чем я мог. MI также может привести к более сложному коду, и многие разработчики OO утверждают, что вам не нужно MI, и если вы его используете, ваша модель, вероятно, ошибочна. Я не уверен, что согласен с этим последним моментом, но держать вещи просто – это всегда хороший план.

В C ++ множественное наследование было основной головной болью при неправильном использовании. Чтобы избежать этих популярных проблем дизайна, несколько интерфейсов «наследование» были вынуждены вместо этого использоваться на современных языках (java, C #).

Множественное наследование

  • сложно понять
  • трудно отлаживать (например, если вы смешиваете classы из нескольких фреймворков, которые имеют одинаково названные методы в глубину, могут возникнуть совершенно неожиданные синергии)
  • легко ошибочно использовать
  • на самом деле это не полезно
  • трудно реализовать, особенно если вы хотите, чтобы это было сделано правильно и эффективно

Поэтому можно считать разумным выбором не включать множественное наследование в язык Java.

Другая причина заключается в том, что однонаследование делает литье тривиальным, не выдавая инструкций ассемблера (кроме проверки совместимости типов, где это необходимо). Если у вас было множественное наследование, вам нужно выяснить, где в дочернем classе запускается определенный родитель. Таким образом, производительность, безусловно, является перком (хотя и не единственным).

Я принимаю утверждение, что «Множественное наследование не допускается в Java» с щепоткой соли.

Множественное наследование определяется, когда «Тип» наследуется от более чем одного «Типы». И интерфейсы также classифицируются как типы, которые имеют поведение. Таким образом, Java имеет множественное наследование. Просто, что это безопаснее.

Динамическая загрузка classов затрудняет реализацию множественного наследования.

В java фактически они избегали сложности множественного наследования вместо этого, используя одно наследование и интерфейс. Сложность множественного наследования очень высока в такой ситуации, как описано ниже.

алмазная проблема множественного наследования. У нас есть два classа B и C, наследующие от A. Предположим, что B и C переопределяют унаследованный метод и обеспечивают их собственную реализацию. Теперь D наследует от B и C выполнение множественного наследования. D должен наследовать этот переопределенный метод, jvm не может решить, какой переопределенный метод будет использоваться?

В c ++ виртуальные функции используются для обработки, и мы должны делать это явно.

Этого можно избежать с помощью интерфейсов, нет тел методов. Интерфейсы не могут быть инстанцированы – они могут быть реализованы только classами или расширены другими интерфейсами.

Еще в старые времена (70-е годы), когда компьютерная наука была больше наукой и меньше массового производства, у программистов было время подумать о хорошем дизайне и хорошей реализации, и в результате продукты (программы) имели высокое качество (например, дизайн TCP / IP и реализация). В настоящее время, когда все программируют, и менеджеры меняют спецификации до крайних сроков, тонкие проблемы, подобные тем, которые описаны в ссылке на wikipedia со страницы Стив Хей, трудно отследить; поэтому «множественное наследование» ограничено конструкцией компилятора. Если вам это нравится, вы все равно можете использовать C ++ …. и иметь всю свободу, которую вы хотите 🙂

На самом деле при множественном наследовании возникает сложность, если унаследованные classы имеют одинаковую функцию. т.е. у компилятора будет путаница, которую нужно выбрать (проблема с алмазом). Таким образом, в Java эта сложность удалена и дала интерфейс, чтобы получить такую ​​функциональность, как множественное наследование. Мы можем использовать интерфейс

Java имеет концепцию, то есть polymorphism. Существует два типа polymorphismа в java. Есть перегрузка методов и переопределение метода. Среди них переопределение метода происходит с отношением супер- и подclassа. Если мы создаем объект подclassа и вызываем метод суперclassа, и если подclass расширяет более одного classа, какой метод суперclassа следует вызывать?

Или, вызывая конструктор суперclassа super() , какой конструктор суперclassа будет вызван?

Эти решения не могут быть реализованы с помощью современных API-интерфейсов Java. поэтому множественное наследование не разрешено в java.

Множественное наследование не допускается непосредственно в Java, но через интерфейсы разрешено.

Причина:

Multiple Inheritance: вводит больше сложности и двусмысленности.

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

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

Давайте рассмотрим простой пример.

  1. Предположим, что есть 2 classа суперclassа A и B с одинаковыми именами методов, но с разными функциональными возможностями. Через следующий код с (extends) ключевое слово множественное наследование невозможно.

      public class A { void display() { System.out.println("Hello 'A' "); } } public class B { void display() { System.out.println("Hello 'B' "); } } public class C extends A, B // which is not possible in java { public static void main(String args[]) { C object = new C(); object.display(); // Here there is confusion,which display() to call, method from A class or B class } } 
  2. Но через интерфейсы возможно (наследование) ключевое слово множественное наследование.

     interface A { // display() } interface B { //display() } class C implements A,B { //main() C object = new C(); (A)object.display(); // call A's display (B)object.display(); //call B's display } } 

Может ли кто-нибудь точно сказать, почему это запрещено?

Вы можете найти ответ из этой документации

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

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

  1. Что делать, если методы или конструкторы из разных суперclassов создают одно и то же поле?

  2. Какой метод или конструктор будет иметь приоритет?

Несмотря на то, что теперь разрешено множественное наследование состояния, вы все же можете реализовать

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

Множественное наследование реализации (через методы по умолчанию в интерфейсах): Возможность наследовать определения методов из нескольких classов

См. Этот связанный вопрос SE для дополнительной информации:

Множественная неоднозначность наследования с интерфейсом

В C ++ class может наследовать (прямо или косвенно) из более чем одного classа, который называется множественным наследованием .

Однако C # и Java ограничивают classы одиночным наследованием, которые каждый class наследует от одного родительского classа.

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

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

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

 class A { protected: bool flag; }; class B : public A {}; class C : public A {}; class D : public B, public C { public: void setFlag( bool nflag ){ flag = nflag; // ambiguous } }; 

В этом примере элемент данных flag определяется class A Но class D нисходит из class B и class C , которые оба выводятся из A , поэтому по существу доступны две копии flag , поскольку два экземпляра A находятся в иерархии classов D Какой из них вы хотите установить? Компилятор будет жаловаться, что ссылка на flag в D неоднозначна . Одно из исправлений заключается в явном рассогласовании ссылки:

 B::flag = nflag; 

Другое решение – объявить B и C как virtual base classes , а это означает, что в иерархии может существовать только одна копия A, устраняя любую двусмысленность.

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

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

Представьте себе этот пример: у меня есть class Shape1

Он имеет метод CalcualteArea :

 Class Shape1 { public void CalculateArea() { // } } 

Существует еще один class Shape2 который также имеет тот же метод

 Class Shape2 { public void CalculateArea() { } } 

Теперь у меня есть дочерний class Circle, он происходит как от Shape1, так и от Shape2;

 public class Circle: Shape1, Shape2 { } 

Теперь, когда я создаю объект для Circle и вызываю метод, система не знает, какой метод вычисляемой области будет вызываться. Оба имеют одинаковые подписи. Поэтому компилятор запутается. Вот почему множественные наследования не допускаются.

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

Давайте будем гением компьютера.