2010-08-24 20 views
67

Estoy leyendo "The Java Tutorial" (por segunda vez). Acabo de pasar por la sección sobre Interfaces (nuevamente), pero todavía no entiendo cómo las interfaces Java simulan herencia múltiple. ¿Hay una explicación más clara que la que está en el libro?¿Cómo simulan las interfaces Java la herencia múltiple?

+1

las discusiones relacionadas: http://stackoverflow.com/questions/2515477/why-there-is-no-multiple-inheritance-in -java-pero-implementation-multiple-interface, http://stackoverflow.com/questions/1262447/multiple-inheritance-in-java –

+3

@Tom: según "The Java Tutorial," (4ª edición p142) "En Java , una clase puede heredar de una sola clase pero puede implementar más de una interfaz. Por lo tanto, los objetos pueden tener varios tipos: el tipo de su propia clase y los tipos de todas las interfaces que implementan. Esto significa que si una variable es declarado como el tipo de interfaz, su valor puede hacer referencia a cualquier objeto instanciado de cualquier clase que implemente La interfaz." El autor me perdió en la última oración, pero las explicaciones aquí (en su mayoría) lo aclararon. – jacknad

+0

Esta publicación de blog ha explicado cómo lograr herencia múltiple. http://www.erik-rasmussen.com/blog/2006/10/23/multiple-inheritance-in-java/ –

Respuesta

140

Suponga que tiene 2 tipos de cosas en su dominio: Camiones y Cocinas

camiones tienen un método driveTo() y un método Cocinas() cocinero.

Supongamos ahora que Pauli decide vender pizzas desde la parte trasera de un camión de reparto. Él quiere algo donde pueda conducir a() y cocinar() con.

En C++ usaría la herencia múltiple para hacer esto.

En Java que se consideró demasiado peligroso para que pueda heredar de una clase principal, pero puede "heredar" los comportamientos de las interfaces, que son para todos los efectos clases abstractas sin campos o implementaciones de métodos.

Así en Java que tienden a poner en práctica la herencia múltiple utilizando delegaciones:

Pauli subclases de un camión y añade una cocina para el camión en una variable miembro llamada cocina. Implementa la interfaz Kitchen al llamar a kitchen.cook().

class PizzaTruck extends Truck implements Kitchen { 
    Kitchen kitchen; 

    public void cook(Food foodItem) { 
     kitchen.cook(foodItem); 
    } 
} 

Él es un hombre feliz porque ahora puede hacer cosas como;

pizzaTruck.driveTo(beach); 
pizzaTruck.cook(pizzaWithExtraAnchovies); 

Ok, esta historia tonta era hacer el punto de que hay simulación de herencia múltiple, es real la herencia múltiple con la condición de que sólo se puede heredar el contrato, sólo se heredan de las clases base abstractas vacíos que se llaman interfaces.

(actualización con la llegada de las interfaces métodos predeterminados ahora también puede proporcionar un comportamiento a ser heredada)

+18

Gracias. Encontré la historia tonta más útil. – jacknad

+2

De nada. Gracias por la respuesta. –

+7

¿qué sucede si la cocina no es una interfaz sino otra clase? – Bizmarck

5

Es bastante simple. Puede implementar más de una interfaz en un tipo. Entonces, por ejemplo, podría tener una implementación de List que también es una instancia de Deque (y Java lo hace ... LinkedList).

Simplemente no puede heredar implementaciones de varios elementos principales (es decir, extender varias clases). Las declaraciones (firmas de métodos) no son un problema.

+0

Sí, es por eso que "simula" la herencia múltiple. –

+1

@Erik: en realidad * implementa * herencia múltiple, simplemente no herencia múltiple de la implementación. –

+0

@Erick: "Simula" es solo un término útil cuando tienes una noción preconcebida de cómo "debe ser" MI de otro idioma. Incluso entonces es un pensamiento destructivo. Es herencia múltiple, solo hecho de manera única. –

3

No es una simulación de herencia múltiple. En Java no puedes heredar de dos clases, pero si implementas dos interfaces "parece que heredaste de dos clases diferentes" porque puedes usar tu clase como cualquiera de tus dos intefaces.

Por ejemplo

interface MyFirstInteface{ 
    void method1(); 
} 
interface MySecondInteface{ 
    void method2(); 
} 
class MyClass implements MyFirstInteface, MySecondInteface{ 
    public void method1(){ 
     //Method 1 
    } 
    public void method2(){ 
     //Method 2 
    } 

    public static void main(String... args){ 
     MyFirstInterface mfi = new MyClass(); 
     MySecondInterface msi = new MyClass(); 
    } 
} 

Esto funciona y se puede utilizar IMF y MSI, que parece como una herencia múltiple, pero no es porque no hereda nada, que acaba de volver a escribir los métodos públicos proporcionados por el interfaces.

1

No creo que lo hagan.

La herencia es específicamente una relación orientada a la implementación entre las implementaciones. Las interfaces no proporcionan ninguna información de implementación en absoluto, sino que definen un tipo. Para tener herencia, debe heredar específicamente algunos comportamientos o atributos de una clase principal.

Creo que hay una pregunta aquí en alguna parte en concreto sobre el papel de las interfaces y la herencia múltiple, pero no puedo encontrar ahora ...

2

No es justo decir que las interfaces 'simular' la herencia múltiple.

Claro, su tipo puede implementar múltiples interfaces y actuar polimórficamente como muchos tipos diferentes. Sin embargo, obviamente no heredará comportamientos o implementaciones bajo esta disposición.

Mire en general la composición en la que cree que puede necesitar herencia múltiple.

O Una posible solución para lograr algo de herencia múltiple es la interfaz Mixin - http://csis.pace.edu/~bergin/patterns/multipleinheritance.html. ¡Use con cuidado!

10

dadas las dos interfaces siguientes ...

interface I1 { 
    abstract void test(int i); 
} 
interface I2 { 
    abstract void test(String s); 
} 

Podemos implementar ambos con el siguiente código ...

public class MultInterfaces implements I1, I2 { 
    public void test(int i) { 
    System.out.println("In MultInterfaces.I1.test"); 
    } 
    public void test(String s) { 
    System.out.println("In MultInterfaces.I2.test"); 
    } 
    public static void main(String[] a) { 
    MultInterfaces t = new MultInterfaces(); 
    t.test(42); 
    t.test("Hello"); 
    } 
} 

Nosotros no podemos extender dos objetos, pero podemos aplicar dos interfaces.

+0

¿Qué sucede si los dos métodos en I1 e I2 tienen el mismo tipo de argumentos? –

+0

Entonces solo tendrías un método, al compilador no le importará ya que hacen lo mismo. Vea la respuesta aquí http://stackoverflow.com/questions/2801878/implementing-two-interfaces-in-a-class-with-same-method-which-interface-method – david99world

+0

Entendido. Gracias. –

1

Realmente no hay simulación de herencia múltiple en Java.

La gente a veces dice que puede simular herencia múltiple usando Interfaces porque puede implementar más de una interfaz por clase y luego usa composición (en lugar de herencia) en su clase para lograr los comportamientos de las múltiples clases que era tratando de heredar desde el principio.

17

Probablemente esté confundido porque ve herencia múltiple localmente, en términos de una clase heredando detalles de implementación de múltiples padres. Esto no es posible en Java (y a menudo conduce a abusos en los idiomas donde es posible).

Las interfaces permiten la herencia múltiple de tipos, p. Ej. un class Waterfowl extends Bird implements Swimmer puede ser utilizado por otras clases como si fuera un Birdy como si fuera un Swimmer. Este es el significado más profundo de la herencia múltiple: permitir que un objeto actúe como si perteneciera a varias clases diferentes no relacionadas a la vez.

+0

Reforzado por los documentos de Oracle: http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html. Le agradezco mucho por hacer la distinción entre * herencia de tipos * y la definición de herencia más "tradicional", que incluye la implementación. He estado tratando de entender cómo las interfaces se pueden considerar como una forma de herencia múltiple durante días, y ahora está claro. Java simplemente usa una definición de herencia diferente a la esperada. Desearía poder darte 61 votos a favor adicionales, porque creo que es una respuesta más sólida que la actualmente aceptada. – skrrgwasme

3

Necesita ser preciso:

Java permite la herencia múltiple de interfaz, pero la herencia único sencillo de implementación.

Usted hacer herencia múltiple de interfaz en Java como esto:

public interface Foo 
{ 
    String getX(); 
} 

public interface Bar 
{ 
    String getY(); 
} 

public class MultipleInterfaces implements Foo, Bar 
{ 
    private Foo foo; 
    private Bar bar; 

    public MultipleInterfaces(Foo foo, Bar bar) 
    { 
     this.foo = foo; 
     this.bar = bar; 
    } 

    public String getX() { return this.foo.getX(); } 
    public String getY() { return this.bar.getY(); } 
} 
1

Si tiene sentido en su modelo de objetos, por supuesto puede heredar de una clase y aplicar 1 o más interfaces también.

9

Las interfaces no simulan herencia múltiple. Los creadores de Java consideraron que la herencia múltiple es incorrecta, por lo que no existe tal cosa en Java.

Si desea combinar la funcionalidad de dos clases en una composición de objetos de uso único. Es decir.

public class Main { 
    private Component1 component1 = new Component1();  
    private Component2 component2 = new Component2(); 
} 

Y si desea exponer ciertos métodos, definir y hacerles delegar la llamada al controlador correspondiente.

Aquí interfaces pueden venir a mano - si Component1 implementa la interfaz Interface1 y Component2 implementos Interface2, puede definir

class Main implements Interface1, Interface2 

Así que se puede utilizar indistintamente los objetos cuando el contexto lo permite.

2

No lo hacen.

Creo que la confusión proviene de personas que creen que la implementación de una interfaz constituye una forma de herencia. No es así; la implementación puede simplemente estar en blanco, ningún comportamiento es forzado por el acto o garantizado a través de ningún contrato. Un ejemplo típico es la interfaz Clonable, que aunque alude a una gran funcionalidad, que define tan poco es esencialmente inútil y potencialmente peligrosa.

¿Qué heredas al implementar una interfaz? ¡Bubkes! Entonces, en mi opinión, deje de usar las palabras interfaz y herencia en la misma oración. Como dijo Michael Borgwardt, una interfaz no es una definición sino un aspecto.

3

A propósito, la razón por la que Java no implementa la herencia múltiple completa es porque crea ambigüedades. Supongamos que puede decir "A extiende B, C", y luego tanto B como C tienen la función "void f (int)". ¿Qué implementación hereda A? Con el enfoque de Java, puede implementar cualquier cantidad de interfaces, pero las interfaces solo declaran una firma. Entonces, si dos interfaces incluyen funciones con la misma firma, bien, su clase debe implementar una función con esa firma. Si las interfaces que hereda tienen funciones con firmas diferentes, entonces las funciones no tienen nada que ver entre sí, por lo que no se trata de un conflicto.

No digo que esta sea la única forma. C++ implementa la herencia múltiple verdadera estableciendo reglas de precedencia de las cuales la implementación gana. Pero los autores de Java decidieron eliminar la ambigüedad. Ya sea por una creencia filosófica de que esto hizo un código más limpio, o porque no querían hacer todo el trabajo extra, no lo sé.

+0

Corrígeme si me equivoco, pero desde mi comprensión C++ también elimina la posibilidad de agregar miembros virtuales a una clase base sin volver a compilar las subclases, y también pierde la identidad referencial de la clase base. Si B tiene miembro virtual Q, X e Y heredan B y anulan Q por separado, y Z hereda tanto X como Y, el fundir a & Z en a & X y luego & B arrojarán un resultado diferente al convertir a & Z en a & Y y luego & B . – supercat

2

En realidad, puede "heredar" varias clases concretas si implementan interfaces. innerclasses ayuda a lograr ese objetivo:

interface IBird { 
    public void layEgg(); 
} 

interface IMammal { 
    public void giveMilk(); 
} 

class Bird implements IBird{ 
    public void layEgg() { 
     System.out.println("Laying eggs..."); 
    } 
} 

class Mammal implements IMammal { 
    public void giveMilk() { 
     System.out.println("Giving milk..."); 
    } 
} 

class Platypus implements IMammal, IBird { 

    private class LayingEggAnimal extends Bird {} 
    private class GivingMilkAnimal extends Mammal {} 

    private LayingEggAnimal layingEggAnimal = new LayingEggAnimal(); 

    private GivingMilkAnimal givingMilkAnimal = new GivingMilkAnimal(); 

    @Override 
    public void layEgg() { 
     layingEggAnimal.layEgg(); 
    } 

    @Override 
    public void giveMilk() { 
     givingMilkAnimal.giveMilk(); 
    } 

} 

15

Aquí es una manera de lograr la herencia múltiple a través de interfaces en Java.

¿Qué se puede lograr?
clase A extiende B, C // esto no es posible en Java directamente, pero se puede lograr indirectamente.

class B{ 
    public void getValueB(){} 
} 

class C{ 
    public void getValueC(){} 
} 


interface cInterface{ 
    public getValueC(); 
} 

class cChild extends C implemets cInterface{ 
    public getValueC(){ 

     // implementation goes here, call the super class's getValueC(); 

    } 
} 


// Below code is **like** class A extends B, C 
class A extends B implements cInterface{ 
    cInterface child = new cChild(); 
    child.getValueC(); 
} 
+0

Muchas gracias. Esta es la respuesta para mí. – enadun

+0

@challenger Cuando implemente "cInterface", tendríamos que implementar "getValueC()" en la clase A, ¿verdad? – Nick

7

sabes que, viniendo desde la perspectiva de un desarrollador de JavaScript tratando de entender qué diablos está pasando con estas cosas, me gustaría señalar un par de cosas y alguien por favor me diga lo que' Me falta aquí si estoy fuera de lugar.

Las interfaces son realmente simples. Estúpidamente, increíblemente simple. Son tan estúpidamente, increíblemente simples como la gente piensa inicialmente, razón por la cual hay tantas preguntas duplicadas sobre este tema exacto porque la única razón para usarlas ha quedado poco clara por parte de las personas que tratan de hacer más de lo que son y hay es un mal uso generalizado en todas las bases de código del servidor Java al que he estado expuesto.

Entonces, ¿por qué querrías usarlos? La mayoría de las veces no lo harías. Ciertamente no querrás usarlos TODO el tiempo, como muchos parecen pensar. Pero antes de llegar a cuando lo harías, hablemos de lo que NO son.

interfaces no son:

  • de ninguna manera una solución para cualquier tipo de mecanismo de herencia que carece de Java. No tienen nada que ver con la herencia, nunca lo hicieron, y de ninguna manera simulan nada parecido a la herencia.
  • necesariamente algo que te ayude con las cosas que escribiste, tanto como ayuda al otro tipo a escribir algo para que sea interconectado por tus cosas.

Realmente son tan simples como crees que son a primera vista. Las personas hacen un uso incorrecto de manera estúpida todo el tiempo, por lo que es difícil entender cuál es el punto. Es solo validación/prueba. Una vez que haya escrito que algo se ajusta a una interfaz y funciona, eliminar ese código "implementa" no romperá nada.

Pero si está utilizando las interfaces correctamente, no querrá eliminarlo porque tenerlo allí le da al siguiente desarrollador una herramienta para escribir una capa de acceso para otro conjunto de bases de datos o servicios web que desean el resto de su aplicación para continuar utilizando porque saben que su clase fallará hasta que consigan la interfaz 100% completa como se esperaba. Todas las interfaces lo hacen, valida su clase y establece que de hecho ha implementado una interfaz como prometió. Nada mas.

También son portátiles. Al exponer las definiciones de su interfaz, puede darles a las personas que deseen utilizar su código no expuesto un conjunto de métodos para que sus objetos lo utilicen correctamente. No tienen que implementar las interfaces. Podrían anotarlos en un pedazo de papel de bloc de notas y verificarlo dos veces. Pero con la interfaz tiene más garantías de que nada va a intentar funcionar hasta que tenga una versión adecuada de la interfaz en cuestión.

Entonces, ¿alguna interfaz que probablemente nunca se implementará más de una vez? Completamente inutil. ¿Herencia múltiple? Deja de alcanzar ese arcoiris. Java los evita por una razón, en primer lugar, y los objetos compuestos/agregados son más flexibles en muchos sentidos de todos modos. Eso no quiere decir que las interfaces no puedan ayudarlo a modelar de manera que la herencia múltiple lo permita, pero en realidad no es herencia de ninguna forma o forma y no debería verse como tal. Solo garantiza que su código no funcionará hasta que haya implementado todos los métodos que estableció que lo haría.

+0

La herencia de clases combina dos conceptos algo ortogonales: (1) la capacidad de las clases derivadas para usar miembros de la clase base como si fueran propios; (2) la capacidad de una variable del tipo de clase base para contener referencias a objetos de cualquier tipo derivado. El aspecto n. ° 2 es una parte suficientemente importante de la herencia que ciertamente lo llamaría "similar a la herencia" [especialmente desde que el aspecto n. ° 1 es una conveniencia, pero se requiere el aspecto n. ° 2 para OOP]. El propósito esencial de una interfaz es permitir el código con una referencia a algo que se sabe que tiene alguna funcionalidad, * para usar esa funcionalidad *. – supercat

+0

2 se parece más a un aspecto clave para Java OOP debido a otras limitaciones de diseño del lenguaje más que a un aspecto clave de OOP o herencia en general. Aunque descubrí que puedes definir y heredar constantes desde una interfaz en Java y yo llamaría eso más que herencia. –

+0

La relación "X-es-A-Y" definida por herencia especifica que el código que puede aceptar una referencia a una "Y" también debería poder aceptar una referencia a una "X". ¿De qué serviría tal relación si tal sustitución no fuera posible? – supercat

1

Hay casos en que la herencia múltiple resulta ser muy útil y difícil de reemplazar con interfaces sin escribir más código. Por ejemplo, hay aplicaciones de Android que usan clases derivadas de Activity y otras de FragmentActivity en la misma aplicación.Si tiene una característica en particular que desea compartir en una clase común, en Java tendrá que duplicar el código en lugar de dejar que las clases secundarias de Activity y FragmentsActivity deriven de la misma clase SharedFeature. Y la mala aplicación de los genéricos en Java no ayuda, ya sea porque escribiendo al siguiente es ilegal:

public class SharedFeature<T> extends <T extends Activity> 

... 
... 
1

no hay apoyo para la herencia múltiple en Java.

Esta historia de compatibilidad con herencia múltiple mediante el uso de la interfaz es lo que los desarrolladores hemos inventado. La interfaz proporciona flexibilidad que las clases concretas y tenemos la opción de implementar múltiples interfaces usando una sola clase. Esto es por acuerdo, nos estamos adhiriendo a dos planos para crear una clase.

Esto está tratando de acercarse a la herencia múltiple. Lo que hacemos es implementar una interfaz múltiple, aquí no estamos ampliando (heredando) nada. La clase de implementación es la que va a agregar las propiedades y el comportamiento. No está obteniendo la implementación gratis de las clases principales. Simplemente diría que no hay soporte para herencia múltiple en java.

1

Me gustaría señalar algo que me mordió por detrás, viniendo de C++ donde también puede heredar fácilmente muchas implementaciones.

Tener una interfaz "amplia" con muchos métodos significa que tendrá que implementar muchos métodos en sus clases concretas y no puede compartir fácilmente estas entre implementaciones.

Por ejemplo:

interface Herbivore { 
    void munch(Vegetable v); 
}; 

interface Carnivore { 
    void devour(Prey p); 
} 

interface AllEater : public Herbivore, Carnivore { }; 

class Fox implements AllEater { 
    ... 
}; 

class Bear implements AllEater { 
    ... 
}; 

En este ejemplo, Fox y oso no puede compartir una aplicación de base común para ambos métodos es la interfaz munch y devour.

Si las implementaciones de bases se parecen a esto, tal vez querrá usar ellos por Fox y Bear:

class ForestHerbivore implements Herbivore 
    void munch(Vegetable v) { ... } 
}; 

class ForestCarnivore implements Carnivore 
    void devour(Prey p) { ... } 
}; 

Pero no podemos heredar ambos. Las implementaciones base deben ser variables miembro en la clase y los métodos definidos pueden reenviarse a eso. Es decir:

class Fox implements AllEater { 
    private ForestHerbivore m_herbivore; 
    private ForestCarnivore m_carnivore; 

    void munch(Vegetable v) { m_herbivore.munch(v); } 
    void devour(Prey p) { m_carnivore.devour(p); } 
} 

Esto se pone difícil de manejar si interfaces de crecer (es decir, más de 5-10 métodos ...)

Un mejor enfoque es definir una interfaz como una agregación de interfaces:

interface AllEater { 
    Herbivore asHerbivore(); 
    Carnivore asCarnivore(); 
} 

Esto significa que Fox y Bear solo tienen que implementar estos dos métodos, y las interfaces y las clases base pueden crecer independientemente de la interfaz agregada AllEater que concierne a las clases de implementación.

Menos acoplamiento de esta manera, si funciona para su aplicación.

1

También tengo que decir que Java no es compatible con herencia múltiple.

Tiene que diferenciar el significado entre extends y implements palabras clave en Java. Si usamos extends, en realidad estamos heredando la clase después de esa palabra clave. Pero, para simplificar todo, no podemos usar extends más de una vez. Pero puede implementar tantas interfaces como desee.

Si implementa una interfaz, existe la posibilidad cero de que omita la implementación de todos los métodos en cada interfaz (Excepción: implementaciones predeterminadas de los métodos de interfaz introducidos en Java 8) Por lo tanto, ahora es plenamente consciente de lo que es pasando con las cosas que ha incorporado a su nueva clase.

Por qué Java no permite la herencia múltiple es en realidad, la herencia múltiple hace que el código sea algo complejo. A veces, dos métodos de clases principales pueden entrar en conflicto debido a tener las mismas firmas. Pero si se ve obligado a implementar todos los métodos de forma manual, obtendrá una comprensión completa de lo que está sucediendo, como mencioné anteriormente. Hace que tu código sea más comprensible para ti.

Si necesita más información sobre las interfaces de Java, echa un vistazo a este artículo, http://www.geek-programmer.com/introduction-to-java-interfaces/