Interface en Java 8 -Metodo Default- con ejemplos
El proyecto de este post lo puedes descargar pulsando AQUI.
En esta entrada vamos a ha hablar de un cambio bastante interesante que han integrado en Java 8, como es el método "default" de las nuevas interfaces.
Como ya se comento en las entradas en las que hablamos de polimorfismo, una clase "interface" es una clase abstracta pura en la que todos sus métodos son abstractos y por tanto no se pueden implementar en la clase Interface. Pues bien, ahora en Java 8 se ha incluido el método "default" que se puede implementar en la clase interface y que su implementación será común para todas las clases que implementen esa interface. Dicho así, uno puede pensar que con este método se rompe con la premisa de que las clases interface son abstractas puras, pero la inclusión de este nuevo método en las interfaces de java 8 tienen dos propósitos interesantes como son:
- No modificar las clases que usen esa interface.
- Simular una "Seudo Herencia Múltiple", ya que java no dispone de herencia múltiple como tal. Sobre este punto hablaremos más adelante para aclarar ese concepto de seudo herencia múltiple.
El punto 1 (aunque puede sonar chapucero) tiene bastante sentido y más hoy en día en que es muy importante la rápida respuesta al cambio, ya que suponer un caso en el que hayamos desarrollado una interface que la implementan 20 clases y necesitemos a última hora añadir una nueva funcionalidad que deben de tener todas las clases que implementan nuestra interface. Pues en este caso con implementar un método "default" en la interface nos valdría para solucionar el problema y adaptarnos rápidamente al cambio. Esta seria una solución rápida aunque si fuésemos muy puristas, con más tiempo deberíamos reescribir esos métodos en las clases que implementan la interface.
Para ver esto vamos a dar una solución rápida al ejemplo puesto en la entrada de "Polimorfismo en Java -Interface- (Parte II), con ejemplos", en la que simulábamos el comportamiento de la selección española de fútbol. En este ejemplo implementamos el comportamiento expuesto en el siguiente diagrama de clases:
Como vemos tenemos tres clases que implementan una misma interface, pero ahora queremos añadir 2 nuevos comportamientos que son comunes para las 3 clases que implementan la interface como son dar una rueda de prensa (ruedaPrensa()) y hacer un video publicitario (videoPublicitario()). Pues bien para ello nos creamos estos dos métodos en la interface y los implementamos ahi; quedando el diagrama de clases de la siguiente manera:
A nivel de código la interface quedaría implementada de la siguiente manera:
public interface IntegranteSeleccionFutbol { void concentrarse(); void viajar(); void entrenar(); void jugarPartido(); default void ruedaPrensa(){ System.out.println(" da una rueda de prensa (desde la Interface)"); } default void videoPublicitario (){ System.out.println(" hacer un video publicitario (desde la Interface)"); } }
En el método MAIN nos creamos creamos un objeto de cada clase y ejecutamos los métodos implementados en la interface de la siguiente manera:
// ArrayList de objetos SeleccionFutbol. Idenpendientemente de la clase hija a la que pertenezca el objeto public static ArrayList integrantes = new ArrayList(); public static void main(String[] args) { SeleccionFutbol delBosque = new Entrenador(1, "Vicente", "Del Bosque", 60, 28489); SeleccionFutbol iniesta = new Futbolista(2, "Andres", "Iniesta", 29, 6, "Interior Derecho"); SeleccionFutbol raulMartinez = new Masajista(3, "Raúl", "Martinez", 41, "Licenciado en Fisioterapia", 18); integrantes.add(delBosque); integrantes.add(iniesta); integrantes.add(raulMartinez); // RUEDA DE PRENSA CON EL MÉTODO DEFAULT System.out.println("Todos los integrantes dan una rueda de prensa. (Todos ejecutan el mismo método)"); for (SeleccionFutbol integrante : integrantes) { System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> "); integrante.ruedaPrensa(); } // VIDEO PUBLICITARIO CON EL MÉTODO DEFAULT System.out.println("Todos los integrantes graban un video publicitario. (Todos ejecutan el mismo método)"); for (SeleccionFutbol integrante : integrantes) { System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> "); integrante.videoPublicitario(); } ................................... }
Como salida a la ejecución de este fragmento de código tenemos el comportamiento implementado en la clase interface:
Todos los integrantes dan una rueda de prensa. (Todos ejecutan el mismo método) Vicente Del Bosque -> da una rueda de prensa (desde la Interface) Andres Iniesta -> da una rueda de prensa (desde la Interface) Raúl Martinez -> da una rueda de prensa (desde la Interface) Todos los integrantes graban un video publicitario. (Todos ejecutan el mismo método) Vicente Del Bosque -> hacer un video publicitario (desde la Interface) Andres Iniesta -> hacer un video publicitario (desde la Interface) Raúl Martinez -> hacer un video publicitario (desde la Interface)
Como vemos hemos podido implementar un nuevo comportamiento desde una interface y que este se pueda usar en cada una de las clases que implementan esa interface.
Por otro lado hemos dicho que con las nuevas interfaces de java 8 podemos simular una "Seudo Herencia Múltiple". Dicho esto hay que matizar un poco estas palabras de la seudo herencia multiple. Sabemos que la herencia " permite compartir automáticamente los atributos y métodos de la clase padre a las clases hijas". Por otro lado sabemos que Java no soporta herencia múltiple ya que solo permite heredar métodos y atributos de una sola clase; es decir, que solo tiene una clase padre. Con las nuevas interfaces de java 8 podemos hacer que una clase implemente todas las interfaces que quiera y al haber métodos "default" en las interfaces, todas las clases que implementen esa interface tendrán esos métodos, los cuales no tendrám que ser sobreescritos. Dicho en otra palabras; con las interfaces de java 8 podemos tener herencia múltiple aplicada exclusivamente a métodos, pero no a atributos.
En el proyecto en Java 8 que hemos compartido al principio, hemos implementado un ejemplo para ver esto de la seudo herencia múltple. Fijemonos en el siguiente diagrama de clases, en el que vemos que tenemos dos interfaces llamadas "DeportistaInterface" e "IntegranteInterface" y en ellas tenemos dos métodos "default". Vemos que "FutbolistaHM" y "EntrenadorHM" implementan las dos interfaces, por lo tanto tendrán los métodos "default" de las dos interfaces; es decir, que es como si heredasen los métodos de la interface. Por otro lado la clase "MasajistaHM" solo implementa la interface "IntegranteInterface" y con esta solo "hereda" los métodos "default" de esta interface. En resumen hemos simulado una herencia de métodos pero no de atributos que hemos "heredado" de la clase interface.
A continuación vamos a ver como hemos implementado las interfaces:
public interface IntegranteInterface { default void concentrarse() { System.out.println("Concentrarse (Interface IntegranteInterface)"); } default void viajar() { System.out.println("Viajar (Interface IntegranteInterface)"); } } |
public interface DeportistaInterface { default void entrenar() { System.out.println("Entrenar (Interface DeportistaInterface)"); } default void jugarPartido() { System.out.println("Jugar Partido (Interface DeportistaInterface)"); } } |
Vemos como hemos implementado en los métodos default un comportamiento que van a "heredar" las clases que implementen esa interface. A continuación mostramos a implementación de las clases "FutbolistaHM", "EntrenadorHM" y "MasajistaHM":
public class FutbolistaHM extends SeleccionFutbolHM implements DeportistaInterface, IntegranteInterface { private int dorsal; private String demarcacion; // constructor, getter y setter } |
public class EntrenadorHM extends SeleccionFutbolHM implements DeportistaInterface, IntegranteInterface { private int idFederacion; // constructor, getter y setter } |
public class MasajistaHM extends SeleccionFutbolHM implements IntegranteInterface { private String titulacion; private int aniosExperiencia; // constructor, getter y setter } |
Como como cada una de las clases implementa la interface que le interesa. Veamos ahora como cada una de estas clases ejecuta los métodos de la interface con el siguiente fragmento de código escrito en el método MAIN:
// ArrayList de objetos SeleccionFutbol. Idenpendientemente de la clase hija // a la que pertenezca el objeto public static ArrayList integrantes = new ArrayList(); public static void main(String[] args) { SeleccionFutbolHM delBosque = new EntrenadorHM(1, "Vicente", "Del Bosque", 60, 28489); SeleccionFutbolHM iniesta = new FutbolistaHM(2, "Andres", "Iniesta", 29, 6, "Interior Derecho"); SeleccionFutbolHM raulMartinez = new MasajistaHM(3, "Raúl", "Martinez", 41, "Licenciado en Fisioterapia", 18); integrantes.add(delBosque); integrantes.add(iniesta); integrantes.add(raulMartinez); // VIAJE System.out.println("Todos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método)"); for (SeleccionFutbolHM integrante : integrantes) { System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> "); ((IntegranteInterface) integrante).viajar(); } // ENTRENAR System.out.println("nSolo Del Bosque e Iniesta entrenan ya que el masajista no tienen el método entrenar"); System.out.print(delBosque.getNombre() + " " + delBosque.getApellidos() + " -> "); ((DeportistaInterface) delBosque).entrenar(); System.out.print(iniesta.getNombre() + " " + iniesta.getApellidos() + " -> "); ((DeportistaInterface) iniesta).entrenar(); }
Vemos en este fragmento de código que todos son capaces de ejecutar el método "viajar()" de la interface "IntegrantesInterface", ya que todos implementan esta interface y solo los objetos de la clase "FutbolistaHM" y "EntrenadorHM" pueden ejecutar el método "entrenar()" de la interface "DeportistaInterface". Como resultado a este código tenemos lo siguiente:
Todos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método) Vicente Del Bosque -> Viajar (Interface IntegranteInterface) Andres Iniesta -> Viajar (Interface IntegranteInterface) Raúl Martinez -> Viajar (Interface IntegranteInterface) Solo Del Bosque e Iniesta entrenan ya que el masajista no tienen el método entrenar Vicente Del Bosque -> Entrenar (Interface DeportistaInterface) Andres Iniesta -> Entrenar (Interface DeportistaInterface)
Esto es un poco en resumen lo que nos permiten hacer las nuevas interfaces de java 8, que nos permiten ser un poco más agiles a la hora de hacer cambios de última hora y medio simular una herencia multiple.
Buenas tardes. Primero que todo muchas gracias por el tiempo en aclarar las dudas. Por otro lado, en mi servidor tengo java 1.4 y la verdad estoy muy limitado en varias cosas y ahora quiero actualizar el jdk del server y la jvm a 1.8
Mi duda es con respecto a todas las clases que ya estan compiladas en 1.4 tengo que volver a compilar todas las clases en 1.8.. cuales serian mis ventajas y desventaja de actualizar mi jdk..? Gracias
No te voy a decir que esta carente de riesgos pues una migración los tiene, aunque normalmente suelen ser fáciles de localizar y cambiar de una clase deprecada a la nueva. Para compilar de 1.4 a 1.8 si por ejemplo utilizas Maven, en el maven-compiler-plugin tienes que cambiar la versión del source y target del 1.4 al 1.8 (tienes como se hace en https://zeroturnaround.com/rebellabs/what-migrating-to-java-8-will-do-to-your-codebase-a-practical-example/)
¿Deprecada?
No hay necesidad de poner palabras que no tienen el significado requerido, se agradece mucho el aporte pero en este caso la palabra no es la apropiada.
Quizás quisiste decir clase obsoleta, pues deprecar según la RAE significa
Rogar, pedir, suplicar con eficacia o instancia.
Tienes razón Ronny, la palabra en español es «obsoleta».
Siempre procuro usarla, aunque alguna vez se me escapa -como este caso- pues la conversión del anglicismo «deprecate» a «deprecar» es una palabra bastante habitual en el lenguaje que usamos entre quienes programamos (y esta es una de muchas, como «encriptar», que viene de otro anglicismo «encrypt», que hasta 2014 era errónea pues en español no existía y la palabra correcta era «cifrar»; sin embargo, acabó siendo aceptada en la RAE porque era muy usada a nivel técnico).
Lo que he entendido de todo de esto es que en Oracle se han cargado las interfaces y las han vuelto clases abstractas.
Me sigue pareciendo chapucero y creo que si tienes una interfaz y quieres poner métodos implementados, es tan fácil como convertirla en abstracta sin más, y cambiar el «implements» de las hijas por «extends».
Se pierde la multiherencia? si, pero eso tenían que haberlo implementado de la manera normal, no mediante las interfaces.
Por mucho que lo miro, no entiendo el porqué de esta «evolución» en Java8
Buenas.. Como estan? soy nuevo en esto de la POO y tengo dudas..
1 – Por lo que entiendo una interface es una clase abstracta, cierto?
2 – Las clases que implementen esas interface haran heredar todos los metodos de la interface?
3 – los metodos "default" haran que los mismos se hereden si o si? que pasa con los metodos que no tienen el "default", tambien seran heredados?
por ejemplo:
public interface IntegranteSeleccionFutbol {
void concentrarse();
void viajar();
void entrenar();
void jugarPartido();
default void ruedaPrensa(){
System.out.println(" da una rueda de prensa (desde la Interface)");
}
default void videoPublicitario (){
System.out.println(" hacer un video publicitario (desde la Interface)");
}
}
// los 4 primeros metodos se le debera ,necesariamente, dar funcionabilidad en la clase q lo extiende?
Saludos .. y perdon mi ignorancia =)
Una interfaz no es una clase abstracta, son cosas diferentes, si bien ninguna es implementable, una diferencia por ejemplo es que una interfaz sus métodos no pueden tener código pero de una clase abstracta sí.
Las clases que implementen la interface no heredan los métodos sino que los sobrescriben (es decir, que hay que rellenar el contenido de los métodos en la clase que la ha implementado)
Los métodos default de las interfaces son de sobrescritura opcional; los que no son default son de sobrescritura obligatoria.
Muy bueno la explicación de los conceptos, lo único que no me gusta eso de: interfaz = clase abstractas puras, las interfaces son interfaces, las clases abstractas son clases abstractas, estas últimas se heredan, las interface se implementan.
saludos.
Buenos Dias: a conitnuacion algunas dudas que tnego respecto al tema
¿¿¿puedo tener de manera simulatanea la palabra implements y la palabra extends en una clase???
parece que funciona así:
public class LinkInterfacesmultiples extends ClaseP implements PrimeraInterface,SegundaInterface,TerceraInterface{
//soprta interfaces multiples de mas de dos interface }
colocando la herencia primero y la interface despues
¿¿luego cuales son las consecuencias de tener una clase con ambas caracterisiticas??
¿Porque puedo usar el llamado al constructor de herencias super(); , en una clase que implementa una interface y no tiene herencia(extends)
ademas parece que no puedo aplicar herencia (extends) a una clase interface,
ni como hijo(derivado), ni como padre(Base).
una conclusion interesante ya que estoy explorando los limites de ambas caracterisiticas POO
¿Son las interfaces otro tipo de paradigma POO,diferente al de herencia o los otros?
finalmente hice unos campos(atributos de clase) en una interface que al parecer compila automaticamente como final. En principio puedo trabajar con este atributo desde la clase que implementa. Pero al crear un campo(atributo de clase) o variable local en la clase que implementa con el mismo nombre,
ya no trabaja con el campo de la interface, en lugar de ello trabaja con con la variable local o atributo de clase. Y no puedo establecer referencia con el atributo de la interface ni con las palabras claves this. o super. , parece haberse anulado o eliminado para la clase que implementa.¿La pregunta es porque y cual es su significado?
Buenas,
por partes. A la primera pregunta sí se puede tener un implements (interfaz) y un extends (herencia). Es una manera de conseguir herencia «múltiple», aunque también tiene otros usos.
En Java solo se permite heredar de un único padre. Pero se pueden tener todas las interfaces que se quieran, al fin y al cabo son interfaces para su implementación.
La herencia sirve para obtener funcionalidad ya creada. La interfaz para poner funcionalidad en el momento que se necesita.
La palabra «super» sirve para llamar algo del padre, por tanto si heredas podrás llamar directamente a sus métodos como si lo hicieras con un «this». Puedes llamar a «super» en un método que implementa una interfaz, si la clase extiende de un padre (en este caso y para el método, es «super» funciona igual que un método normal, no sobrescrito).
Las interfaces pueden heredar de otras interfaces. Lo que ocurre es que al implementarla en una clase tendrás que dar funcionalidad a todos los métodos de esta.
Se podría decir que es otro tipo de paradigma diferente. Es muy común utilizarlo con la Programación Orientada a Eventos; ahora mismo tenemos información de esto en nuestro libro de Android gratuito en http://jarroba.com/libro-android-100-gratis/ (busca la programación orientada a eventos, está explicado para Java), o el polimorfismo.
Es mejor que no pongas variables en la interfaz (si las pones serán estáticas o finales, pues no se puede implementar una interfaz, solo sus métodos), no es una buena idea. Se suelen utilizar para almacenar ciertas constantes que se necesiten al implementar la interfaz, pero no suele ser habitual tener que utilizarlas.
Buenos Dias: Gracias por responder mis preguntas
Noobstante,
Mira es que estuve probando.
y la palabra super(); //la cual llama a un constructor de la clase padre, y si se usa esta debe ser la primera linea de codigo en el constructor de la clase hija.
probe colocando la super(); en una clase que implementa una interfaz(sin herencia (sin extends)), y tambien
en una clase normal SIN herencia
y no me presenta errores de compilacion ,
ademas de permitir la ejecucion normal del programa.
que raro ¿!No¡?.
¿se puede colocar, pero se diria que no hace nada?
se me hizo extraño porque ademas las interfaces no admiten cosntructores.
Sí, el método super(), es para llamar a los constructores de la clase padre (echa un vistazo a http://jarroba.com/herencia-en-la-programacion-orientada-a-objetos-ejemplo-en-java/).
Ahora no recuerdo si se puede utilizar en una clase sin herencia, pero si te deja, piensa que estás llamando a un método vacío. De ahí que no te de errores y no haga nada 😉
Cuando usas super en el constructor de una clase que no "hereda" de nadie, en realidad estás mandando a llamar al construcctor de la clase Object.
No te genera errores por que el compilador lo hace siempre de manera implicita, y al momento de que tu lo declares, solo es redundante (no es que no estes llamando a algo vacio).
Muy cierto Alan, la clase que está por encima es Object siempre que no se herede de nada. Fallo mío 😉
Gracias por tomar tu timepo escribir tan interesantes articulos,
me gustaria saber mas sobre los cambios que trae JAVA 8.