¿Por qué no se permite la herencia múltiple en Java o C #?

Sé que no se permite la herencia múltiple en Java y C #. Muchos libros solo dicen que la herencia múltiple no está permitida. Pero puede implementarse mediante el uso de interfaces. No se discute nada sobre por qué no está permitido. ¿Alguien puede decirme exactamente por qué no está permitido?

La respuesta corta es: porque los diseñadores del lenguaje decidieron no hacerlo.

Básicamente, parecía que los diseñadores de .NET y Java no permitían la herencia múltiple porque pensaban que agregar MI agregaba demasiada complejidad a los lenguajes a la vez que proporcionaba muy poco beneficio .

Para una lectura más divertida y profunda, hay algunos artículos disponibles en la web con entrevistas de algunos de los diseñadores de idiomas. Por ejemplo, para .NET, Chris Brumme (que trabajó en MS en el CLR) explicó las razones por las que decidieron no hacerlo:

  1. Los diferentes idiomas en realidad tienen diferentes expectativas sobre cómo funciona MI. Por ejemplo, cómo se resuelven los conflictos y si las bases duplicadas se fusionan o son redundantes. Antes de que podamos implementar MI en el CLR, tenemos que hacer una encuesta de todos los idiomas, descubrir los conceptos comunes y decidir cómo expresslos en una lengua neutral. También tendríamos que decidir si MI pertenece al CLS y lo que esto significaría para los idiomas que no quieren este concepto (presumiblemente VB.NET, por ejemplo). Por supuesto, ese es el negocio en el que nos encontramos como tiempo de ejecución de lenguaje común, pero aún no lo hemos logrado para MI.

  2. El número de lugares donde MI es realmente apropiado es realmente bastante pequeño. En muchos casos, la herencia de interfaz múltiple puede hacer el trabajo en su lugar. En otros casos, es posible que pueda usar encapsulación y delegación. Si tuviéramos que agregar una construcción ligeramente diferente, como mixins, ¿sería realmente más poderosa?

  3. La herencia de implementación múltiple inyecta mucha complejidad en la implementación. Esta complejidad afecta la fundición, el diseño, el despacho, el acceso al campo, la serialización, las comparaciones de identidad, la verificabilidad, la reflexión, los generics y, probablemente, muchos otros lugares.

Puedes leer el articulo completo aquí.

Para Java, puedes leer este artículo :

Las razones para omitir la herencia múltiple del lenguaje Java provienen principalmente del objective “simple, orientado a objetos y familiar”. Como un lenguaje simple, los creadores de Java querían un lenguaje que la mayoría de los desarrolladores pudieran comprender sin una capacitación extensa. Para ello, trabajaron para que el lenguaje fuera lo más similar posible a C ++ (familiar) sin sobrecargar la complejidad innecesaria de C ++ (simple).

En opinión de los diseñadores, la herencia múltiple causa más problemas y confusiones de las que resuelve. Entonces cortaron herencia múltiple del lenguaje (del mismo modo que redujeron la sobrecarga del operador). La amplia experiencia de los diseñadores en C ++ les enseñó que la herencia múltiple no valía la pena.

La herencia múltiple de la implementación es lo que no está permitido.

El problema es que el comstackdor / tiempo de ejecución no puede determinar qué hacer si tiene una clase Cowboy y una clase Artist, ambas con implementaciones para el método draw (), y luego intenta crear un nuevo tipo CowboyArtist. ¿Qué sucede cuando llamas al método draw ()? ¿Alguien está muerto en la calle, o tienes una hermosa acuarela?

Creo que se llama el problema de herencia de doble diamante.

Motivo: Java es muy popular y fácil de codificar, debido a su simplicidad.

Entonces, lo que sea que a los progtwigdores java les parezca difícil y complicado de entender, trataron de evitarlo. Uno de esos tipos de propiedad es la herencia múltiple.

  1. Ellos evitaron punteros
  2. Evitaban la herencia múltiple.

Problema con la herencia múltiple: problema de diamantes.

Ejemplo :

  1. Supongamos que la clase A está teniendo un método divertido (). la clase B y la clase C derivan de la clase A.
  2. Y ambas clases B y C, anulan el método fun ().
  3. Ahora suponga que la clase D hereda ambas clases B y C. (solo Asunción)
  4. Crear objeto para la clase D.
  5. D d = nuevo D ();
  6. y tratar de acceder a d.fun (); => ¿llamará a la diversión de la clase B () o la diversión de la clase C ()?

Esta es la ambigüedad existente en el problema de los diamantes.

No es imposible resolver este problema, pero crea más confusión y complejidades para el progtwigdor mientras lo lee. Causa más problemas de los que intenta resolver.

Nota : Pero de cualquier forma siempre puede implementar herencia múltiple indirectamente mediante el uso de interfaces.

Porque Java tiene una filosofía de diseño muy diferente de C ++. (No voy a discutir C # aquí).

Al diseñar C ++, Stroustrup quería incluir características útiles, independientemente de cómo podrían ser mal utilizadas. Es posible arruinarlo a gran velocidad con herencia múltiple, sobrecarga de operadores, plantillas y varias otras características, pero también es posible hacer algunas cosas muy buenas con ellos.

La filosofía de diseño de Java es enfatizar la seguridad en los constructos del lenguaje. El resultado es que hay cosas que son mucho más difíciles de hacer, pero puede estar mucho más seguro de que el código que está viendo significa lo que cree que es.

Además, Java fue en gran medida una reacción de C ++ y Smalltalk, los lenguajes OO más conocidos. Hay muchos otros lenguajes OO (Common Lisp fue en realidad el primero en ser estandarizado), con diferentes sistemas OO que manejan mejor MI.

Sin mencionar que es completamente posible hacer MI en Java, usando interfaces, composición y delegación. Es más explícito que en C ++, y por lo tanto, es más difícil de usar, pero le proporcionará algo que es más probable que comprenda a primera vista.

No hay una respuesta correcta aquí. Hay diferentes respuestas, y cuál es mejor para una situación dada depende de las aplicaciones y las preferencias individuales.

La razón principal (aunque de ninguna manera la única) por la que las personas se alejan de MI es el llamado “problema de los diamantes” que conduce a la ambigüedad en su implementación. Este artículo de wikipedia lo analiza y explica mejor que yo. MI también puede conducir a un código más complejo, y muchos diseñadores de OO afirman que no necesita un MI, y si lo usa, su modelo probablemente sea incorrecto. No estoy seguro de estar de acuerdo con este último punto, pero mantener las cosas simples siempre es un buen plan.

En C ++, la herencia múltiple era un gran dolor de cabeza cuando se usa de forma incorrecta. Para evitar los problemas de diseño populares, se forzaron múltiples interfaces “herencia” en lugar de idiomas modernos (java, C #).

Herencia múltiple es

  • difícil de entender
  • difícil de depurar (por ejemplo, si mezcla clases de múltiples marcos que tienen métodos idénticos en el fondo, pueden ocurrir sinergias bastante inesperadas)
  • fácil de usar mal
  • realmente no es tan útil
  • Difícil de implementar, especialmente si quieres que se haga de manera correcta y eficiente

Por lo tanto, se puede considerar una buena opción no incluir Herencia Múltiple en el lenguaje Java.

Otra razón es que la herencia única hace que la conversión sea trivial y no emite instrucciones de ensamblador (aparte de verificar la compatibilidad de los tipos cuando sea necesario). Si tuvieras herencia múltiple, necesitarías averiguar en qué parte de la clase infantil comienza un padre determinado. Entonces, el rendimiento es sin duda un beneficio (aunque no el único).

Tomo la statement de que “Herencia múltiple no está permitida en Java” con una pizca de sal.

Herencia múltiple se define cuando un “Tipo” hereda de más de un “Tipos”. Y las interfaces también se clasifican como tipos ya que tienen un comportamiento. Entonces Java tiene herencia múltiple. Solo que es más seguro.

La carga dinámica de clases dificulta la implementación de herencia múltiple.

En Java, en su lugar, evitaron la complejidad de la herencia múltiple mediante el uso de una sola herencia e interfaz. La complejidad de la herencia múltiple es muy alta en una situación como la explicada a continuación

problema de diamantes de herencia múltiple Tenemos dos clases B y C que heredan de A. Supongamos que B y C están anulando un método heredado y proporcionan su propia implementación. Ahora D hereda de B y C haciendo herencia múltiple. D debe heredar ese método reemplazado, jvm no puede decidir qué método reemplazado se usará?

En c ++ se usan funciones virtuales para manejar y tenemos que hacerlo explícitamente.

Esto se puede evitar mediante el uso de interfaces, no hay cuerpos de métodos. No se pueden crear instancias de interfaces: solo pueden implementarse por clases o extenderse por otras interfaces.

En los viejos tiempos (’70) cuando la informática era más ciencia y menos producción en masa, los progtwigdores tuvieron tiempo para pensar en un buen diseño y una buena implementación y, como resultado, los productos (progtwigs) tenían alta calidad (por ejemplo, diseño TCP / IP e implementación ). Hoy en día, cuando todo el mundo está progtwigndo, y los gerentes están cambiando las especificaciones antes de los plazos, cuestiones sutiles como la descrita en el enlace de la wikipedia de la publicación de Steve Haigh son difíciles de rastrear; por lo tanto, la “herencia múltiple” está limitada por el diseño del comstackdor. Si te gusta, puedes usar C ++ … y tener toda la libertad que quieras 🙂

En realidad, la herencia múltiple surgirá a la complejidad si las clases heredadas tienen la misma función. es decir, el comstackdor tendrá una confusión que uno debe elegir (problema de diamante). Así que en Java esa complejidad se eliminó y dio interfaz para obtener la funcionalidad como herencia múltiple. Podemos usar la interfaz

Java tiene un concepto, es decir, polymorphism. Hay 2 tipos de polymorphism en java. Hay sobrecarga de método y anulación de método. Entre ellos, la anulación de método ocurre con la relación de superclase y subclase. Si estamos creando un objeto de una subclase e invocando el método de superclase, y si la subclase extiende más de una clase, ¿qué método de superclase debería llamarse?

O bien, al llamar al constructor de superclase por super() , ¿a qué constructor de superclase se le llamará?

Estas decisiones son imposibles por las características actuales de la API de Java. por lo que la herencia múltiple no está permitida en Java.

La herencia múltiple no está permitida en Java directamente, pero a través de las interfaces está permitido.

Motivo:

Herencia múltiple: presenta más complejidad y ambigüedad.

Interfaces: las interfaces son clases completamente abstractas en Java que le proporcionan una manera uniforme de delinear adecuadamente la estructura o el funcionamiento interno de su progtwig desde su interfaz de acceso público, con la consecuencia de una mayor cantidad de flexibilidad y código reutilizable, así como más control sobre cómo creas e interactúas con otras clases.

Más precisamente, son una construcción especial en Java con la característica adicional que le permite realizar un tipo de herencia múltiple, es decir, clases que pueden ser actualizadas a más de una clase.

Tomemos un ejemplo simple.

  1. Supongamos que hay 2 clases de superclases A y B con los mismos nombres de método pero diferentes funcionalidades. A través del siguiente código con la palabra clave (extends), la herencia múltiple no es posible.

      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. Pero a través de las interfaces, con la palabra clave (implements) es posible la herencia múltiple.

     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 } } 

¿Alguien puede decirme exactamente por qué no está permitido?

Puede encontrar la respuesta de este enlace de documentación

Una razón por la cual el lenguaje de progtwigción Java no le permite extender más de una clase es evitar los problemas de la herencia múltiple de estado, que es la capacidad de heredar campos de múltiples clases

Si se permite la herencia múltiple y cuando se crea un objeto instanciando esa clase, ese objeto heredará campos de todas las superclases de la clase. Causará dos problemas.

  1. ¿Qué sucede si los métodos o constructores de diferentes súper clases instancian el mismo campo?

  2. ¿Qué método o constructor tendrá prioridad?

Aunque ahora se permite la herencia múltiple de estado, aún puede implementar

Herencia múltiple de tipo : capacidad de una clase para implementar más de una interfaz.

Herencia múltiple de implementación (a través de métodos predeterminados en las interfaces): Capacidad de heredar definiciones de métodos de múltiples clases

Consulte esta pregunta SE relacionada para obtener información adicional:

Múltiple ambigüedad de herencia con la interfaz

En C ++, una clase puede heredar (directa o indirectamente) de más de una clase, lo que se conoce como herencia múltiple .

C # y Java, sin embargo, limitan las clases a la herencia individual que cada clase hereda de una única clase principal.

La herencia múltiple es una forma útil de crear clases que combinan aspectos de dos jerarquías de clases dispares, algo que a menudo sucede cuando se utilizan marcos de clase diferentes dentro de una sola aplicación.

Si dos marcos definen sus propias clases base para excepciones, por ejemplo, puede usar herencia múltiple para crear clases de excepción que se pueden usar con cualquiera de los marcos.

El problema con la herencia múltiple es que puede generar ambigüedad. El ejemplo clásico es cuando una clase hereda de otras dos clases, cada una de las cuales hereda de la misma clase:

 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 } }; 

En este ejemplo, el miembro de datos de flag está definido por la class A Pero la class D desciende de la class B y la class C , que derivan de A , por lo tanto, en esencia, dos copias de la flag están disponibles porque dos instancias de A están en la jerarquía de clases de D ¿Cuál quieres establecer? El comstackdor se quejará de que la referencia al flag en D es ambigua . Una solución es eliminar la ambigüedad explícita de la referencia:

 B::flag = nflag; 

Otra solución es declarar B y C como virtual base classes , lo que significa que solo una copia de A puede existir en la jerarquía, eliminando cualquier ambigüedad.

Existen otras complejidades con la herencia múltiple, como el orden en que se inicializan las clases base cuando se construye un objeto derivado, o la manera en que los miembros pueden ocultarse inadvertidamente de las clases derivadas. Para evitar estas complejidades, algunos lenguajes se restringen al modelo de herencia simple más simple.

Aunque esto simplifica considerablemente la herencia, también limita su utilidad porque solo las clases con un ancestro común pueden compartir comportamientos. Las interfaces mitigan esta restricción en cierta medida al permitir que las clases en diferentes jerarquías expongan interfaces comunes, incluso si no se implementan compartiendo el código.

Imagina este Ejemplo: Tengo una clase Shape1

Tiene el método CalcualteArea :

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

Hay otra clase Shape2 que también tiene el mismo método

 Class Shape2 { public void CalculateArea() { } } 

Ahora tengo un círculo de clase infantil, deriva de Shape1 y Shape2;

 public class Circle: Shape1, Shape2 { } 

Ahora cuando creo un objeto para Circle y llamo al método, el sistema no sabe qué método de área de cálculo se va a llamar. Ambos tienen las mismas firmas. Entonces el comstackdor se confundirá. Es por eso que no se permiten herencias múltiples.

Pero puede haber múltiples interfaces porque las interfaces no tienen definición de método. Incluso ambas interfaces tienen el mismo método, ambas no tienen ninguna implementación y siempre se ejecutará el método en la clase hija.