Una de las novedades que trae Java 8 a nivel del lenguaje, son los Default Method o Métodos default, los cuales permiten agregar métodos implementados en las interfaces. Antes de la versión 8 de Java era totalmente imposible definirles cuerpo a los métodos de las interfaces ya que nos marcaba error de compilación.
Para agregar un método default solo será necesario marcar un método con el operador default , veamos un ejemplo de cómo se implementan:
public default double getTotal(){ return getQuantity() * getPrice(); }
Los métodos default se utilizan cuando es necesario implementar un cuerpo sin la necesidad de tener que implementar por separado una clase abstracta, además los métodos por default se pueden sobrescribir en caso de que la implementación por default no cumpla con lo que requerimos. Tenemos que resaltar que los métodos default no remplazan a las clases abstractas, pues estas últimas son mucho más potentes, debido a que nos permiten definir propiedades a nivel de clase, constructores y métodos privados. Veamos una limitante de los métodos default:
public default double getTotal(){ return quantity * price; }
Esta interface nos va a marcar error en tiempo de compilación, debido a que no es posible declarar propiedades de clase y mucho menos interactuar con ellas, por lo que estamos limitados a trabajar con los parámetros o con los métodos definidos en la misma interface o que extiende de otra.
Un ejemplo práctico:
Imaginemos que estamos programando un sistema que procesa ordenes de varios tipos, cómo el caso de un ERP, este sistema, podría crear varios tipos de órdenes, como una orden de compra, venta, embarque, etc. todas estas órdenes podrían tener algo en común, cómo, por ejemplo, un total, una lista de ítems asociados a la orden y los ítems pueden tener una cantidad de productos y un precio, así mismo los detalles tendrían un total.
El código completo de este articulo lo puedes encontrar en GitHub: https://github.com/oscarjb1/DefaultMethod.git
Lo interesante aquí es que si todas las ordenes tiene un total, ¿no sería bueno tener una definición del método por default que nos calcule el total y a la vez los ítems también implementen su método para obtener el total? De la misma forma, si una orden tiene ítems, no sería bueno definir un método que permite agregar los ítems.
Veamos cómo quedaría si implementáramos los métodos por default, implementaremos dos interfaces, una que representa una order (IOrder ) y otra que representa los ítems (IOrderItem ):
package com.osb.defaultmethod; import java.util.List; /** * @author Oscar Blancarte */ public interface IOrder { public List<IOrderItem> getOrderItem(); public default double getTotal(){ double total = 0; for (IOrderItem item : getOrderItem()) { total += item.getTotal(); } return total; } public default void addOrderItem(IOrderItem orderItem){ getOrderItem().add(orderItem); } }
Analicemos los métodos de la clase IOrder:
- getOrderItem: método que nos regresará los ítems que tiene la orden, no lo podemos implementar porque no es posible definir propiedades a nivel de clase, salvo constantes. Por lo que la clase que la implemente deberá de regresar una lista de ítems.
- getTotal: este método nos regresará el total de la orden, es decir la sumatoria de todos los ítems de la orden. Cuanta con el operador default lo que nos permite implementarlo recorriendo todos los ítems de la orden y sumando su total.
- addOrderItem: método default que nos permite agregar fácilmente nuevos ítems a la orden.
package com.osb.defaultmethod; /** * @author Oscar Blancarte */ public interface IOrderItem { public double getQuantity(); public double getPrice(); public default double getTotal(){ return getQuantity() * getPrice(); } }
Esta segunda interface representa los ítems de la orden, cuanta con los métodos:
- getQuantity: deberá retornar la cantidad de productos que tiene la línea.
- getPrice: deberá de retornar el precio por producto.
- getTotal: método default que calcula el total de la línea, multiplicando la cantidad por el precio.
Hasta este punto ya hemos implementado todos los métodos default y continuaremos creando un par de clases concretas que implementen estas dos interfaces para ver cómo funcionaría en tiempo de ejecución. Para esto crearemos las siguientes clases:
package com.osb.defaultmethod; import java.util.ArrayList; import java.util.List; /** * @author Oscar Blancarte */ public class SalesOrder implements IOrder{ private List<IOrderItem> orderItems; @Override public List<IOrderItem> getOrderItem() { if(orderItems==null){ orderItems = new ArrayList<>(); } return orderItems; } }
En esta clase representará una orden de venta, por lo cual deberá de implementar la interface IOrder , pero como podemos ver, solo nos pedirá que implementemos el método getOrderItem el cual es el único que no es default, los métodos getTotal y addOrderItem ya estarán implementados por default y se podrán utilizar sin necesidad de declararlos en la clase.
El método getOrderItem solo regresa la lista de ítems, en caso de que la lista sea nula la crea y la retorna.
La siguiente clase representa los ítems de la orden de venta:
package com.osb.defaultmethod; /** * @author Oscar Blancarte */ public class SalesOrderItem implements IOrderItem { private double quantity; private double price; public SalesOrderItem(double quantity, double price) { this.quantity = quantity; this.price = price; } @Override public double getQuantity() { return quantity; } @Override public double getPrice() { return price; } }
Podemos observar que implementan la interface IOrderItem la cual nos pedirá que implementemos los métodos getQuantity y getPrice los cuales ya hemos explicado. El método getTotal ya está implementado por default desde la interface, por lo que ya no será necesario implementarlo en la clase.
Finalmente, la clase Main , en donde pondremos a prueba lo que hemos aprendido para ver cómo se comportan los métodos por default en tiempo de ejecución:
package com.osb.defaultmethod; /** * @author Oscar Blancarte */ public class Main { public static void main(String[] args) { SalesOrder salesOrder = new SalesOrder(); salesOrder.addOrderItem(new SalesOrderItem(10, 100)); salesOrder.addOrderItem(new SalesOrderItem(1, 20)); System.out.println("total ==> " +salesOrder.getTotal() ); } }
Lo primero que hacemos es crear una instancia de la Orden de venta (línea 8), luego en las líneas 9 y 10 agregamos dos nuevos ítems. Notemos que el método addOrderItem esta implementado como default en la clase IOrde . Cada ítem define un precio y la cantidad.
Finalmente, en la línea 11 imprimimos el total de la orden, el total de la orden es calculado desde el método default de la clase IOrder sumando el total de los ítems y el total de cada ítem también es calculado mediante el método default implementado desde la interface IOrderItem .
Como resultado de la ejecución tendremos:
total ==> 1020.0
Conclusiones:
Los métodos default son sin duda una gran utilidad, sobre todo porque nos evita tener que tener que implementar clases abstractas, pero como ya lo mencionamos no las sustituyen.
Hola, muy interesante tu blog, la verdad no tenia idea de está novedad en java 8.
Creo que falta algún menu para encontrar mas fácil los articulos que publicas.
Compré tu libro 🙂 me pareció bastante práctico, hace menos de 1 hora que lo compré, dentro de esta semana comienzo a leerlo.
Saludos!
Wow, muchas gracias por depositar tu confianza en mí, a través de la compra del libro. la verdad que me da mucho gusto saber de ti, ya que las personas que compran por medio de Amazon son anónimas para el Autor, me gustaría seguir en contacto contigo y que me cuentes como te ha parecido el libro, que se puedo mejorar y sobre si fue te tu agrado.
Con respecto a lo de Java 8, la verdad es que agregaron algunas mejoras considerables, pero sobresale por mucho el tema de los Lambda Expresion. En estas semanas estaré subiendo más contenido de las mejoras de Java 8, por lo que te invito a seguir mi blog y sobre todo seguirme en Twitter (@oscarjblancarte) para que te lleguen todas las actualizaciones.
Recuerda que cualquier duda que tengas del libro me la puedes consultar en la página del libro o también en mi twitter.
¡Un saludo y seguimos en contacto!! ¿Por cierto de que pías nos visitas?