¿Qué son las interfaces Java 8 y cómo se utilizan?

En Java, una interfaz funciona de la misma forma que una clase abstracta, lo que significa que nunca se puede crear un objeto a partir de una interfaz. Como resultado de esto, una interfaz no tiene constructores, pero tiene métodos.

Antes de Java 8, una interfaz podía describir las operaciones que realiza su clase de implementación, pero no cómo la clase de implementación debería realizar esta operación (más sobre esto más adelante). Ahora, con Java 8, un desarrollador puede hacer ambas cosas con una interfaz.

Esto se debe a que Java 8 introdujo métodos de interfaz predeterminados. Por lo tanto, los desarrolladores ahora tienen la opción de usar métodos abstractos tradicionales, así como los nuevos métodos concretos dentro de una interfaz. Miremos más de cerca.

¿Qué es una interfaz?

Antes de que pueda comprender realmente el propósito de una interfaz y cómo usarla de manera efectiva, deberá comprender el polimorfismo. El polimorfismo es un concepto central de la programación orientada a objetos, que permite a un desarrollador crear un comportamiento generalizado y especializado con clases (que, en algunos casos, no están directamente relacionadas).

El principal beneficio del polimorfismo es que facilita la reutilización de código, que es vital en la industria de desarrollo de software actual.

Relacionado: ¿Qué es el polimorfismo? Y por qué vale la pena aprender

Entonces, ¿cómo se relaciona el polimorfismo con una interfaz Java? En Java, una interfaz permite que las clases que no estarían relacionadas convencionalmente tengan acceso a operaciones similares. En su forma más simple, una interfaz Java es una plantilla que puede ser utilizada fácilmente por las diferentes clases que la implementan. Estas clases de implementación pueden transformar el método de una interfaz de su estado generalizado a un estado más especializado para realizar una tarea específica.

¿Cuáles son las ventajas de utilizar interfaces Java 8?

El beneficio más obvio de usar una interfaz Java 8 es su nueva capacidad de método concreto.

Otro beneficio bienvenido de las interfaces Java 8 es la capacidad de agregar nuevos métodos concretos a una interfaz existente, que ya se ha implementado, sin romper el programa. Antes de Java 8, si tenía una clase que implementaba una interfaz pero no usaba todos sus métodos, esa clase tendría que etiquetarse como abstracta. De lo contrario, el programa se rompería.

¿Cuándo debería utilizar una interfaz?

Muchos programas existentes pueden utilizar potencialmente interfaces Java 8. La función de una interfaz es facilitar la vida de un programador. Aunque se puede lograr la misma funcionalidad sin interfaces, usarlas hará que sus programas estén más organizados y su proceso de desarrollo requiera menos tiempo.

Si está familiarizado con la herencia, debe saber que también es un concepto central de la programación orientada a objetos que facilita la generalización. Con la herencia, las clases similares se agrupan en relaciones padre-hijo.

Con el polimorfismo (que es el comportamiento de programación que exhibe una interfaz), las clases que generalmente no tendrían acceso a los mismos métodos (debido a la falta de relaciones entre padres e hijos), ahora pueden procesarse polimórficamente.

Un ejemplo práctico del uso de una interfaz se encuentra en el departamento de cuentas de una organización de software. Es muy probable que este departamento lleve a cabo un conjunto de los mismos métodos (u operaciones) al crear el recibo de pago de un empleado y la factura de un cliente.

Estas son dos clases que no estarían relacionadas convencionalmente, pero ahora pueden usar algunas de las mismas operaciones gracias a las interfaces de Java 8.

Crear una interfaz en Java

Con el escenario del departamento de cuentas anterior, puede crear una interfaz que se ocupe de las operaciones de pago. El propósito de esta interfaz es ayudar con la generación de informes de pago (en forma de facturas, nóminas y otros gastos).

Creación de un ejemplo de interfaz Java

 //Java interface
public interface Payable {
//abstract method public void paymentAmount();
}

El código anterior genera una interfaz Java simple. La palabra clave de la interfaz indica que Payable es una interfaz y el método paymentAmount () es un método abstracto porque no está implementado.

Implementación del ejemplo de interfaz de pago

 public class Employee implements Payable {
//attributes
private String name; private String position;
//primary constructor
public Employee (String name, String position) {
this.name = name; this.position = position;
}
//payLevel method - takes an employee's position and returns their pay
public double payLevel(String position) {
position.toLowerCase();
if (position == "junior" ) {
return 10.00;
}
if(position == "mid-level") {
return 20.0;
}
if(position == "senior") {
return 30.00;
}
return 0 ;
}
@Override public void paymentAmount() {
//passes the position attribute provided by the user to the payLevel() method above
// store the return from the function call to a double variable called pay
//and print the relevant data to the console
double pay = payLevel(position);
System.out.println(name + " pay for this month is: " + pay);
}
}

Para usar una interfaz en una clase, primero deberá implementar esa interfaz usando la palabra clave implementar , como puede ver en el código anterior. Después de implementar una nueva interfaz, debe crear métodos concretos para todos los métodos abstractos en la interfaz, utilizando la palabra clave @Override .

Relacionado: Polimorfismo en Java: cómo sobrecargar o anular métodos

Ejecución del ejemplo de programa

 public class Main {
public static void main(String[] args) {
//Create a new employee pay report
Payable JanesPay = new Employee ("Jane Doe", "mid-level");
//calculate the employee's pay
JanesPay.paymentAmount();
}
}

El programa anterior usa la clase Empleado para generar un nuevo recibo de pago que utiliza la interfaz Pagable . No se puede crear un objeto desde una interfaz, pero un objeto se puede declarar usando una interfaz, como puede ver en el código anterior.

El objeto de pago de Jane se crea utilizando dos atributos: el nombre de un empleado y el puesto del empleado en la empresa. Solo hay un constructor principal en la clase de empleado , por lo tanto, cada nuevo objeto de empleado que se cree debe tener dos atributos de cadena.

Usando el objeto de pago de Jane , el programa llama al método paymentAmount () en la clase de empleado , que produce el siguiente resultado en la consola:

 Jane Doe pay for this month is: 20.0

Implementación de la interfaz de pago con el ejemplo del cliente

 public class Customer implements Payable{
private String customerName;
private String projectType;
public Customer(String customerName, String projectType) {
this.customerName = customerName;
this.projectType = projectType;
}
public double customerInvoice(String projectType) {
projectType.toLowerCase();
if (projectType == "small" ) {
return 10.00;
}
if(projectType == "medium") {
return 20.0;
}
if(projectType == "large") {
return 30.00;
}
return 0 ;
}
@Override public void paymentAmount() {
double total = customerInvoice(projectType);
System.out.println(customerName + " total charge for services provided is: " + total);
}
}

Las clases de empleado y cliente no están relacionadas entre sí, pero debido a que todas implementan la misma interfaz, ahora tienen acceso a operaciones similares. Al igual que la clase de empleado , la clase de cliente tiene que implementar la interfaz y anular el método abstracto paymentAmount () para que funcione correctamente.

Ahora, las mismas operaciones que se realizaron en un objeto de la clase de empleado , también se pueden realizar en un objeto de la clase de cliente .

Creación de un ejemplo de objeto de cliente

 public class Main {
public static void main(String[] args) {
//Create a new customer invoice report
Payable PaulsInvoice = new Customer("Paul Smith", "large");
//calculate the customer's pay
PaulsInvoice.paymentAmount();
}
}

El código anterior genera la siguiente salida en la consola:

 Paul Smith total charge for services provided is: 30.0

Debido a que la interfaz es una interfaz Java 8, puede agregarle métodos predeterminados sin romper el código, como puede ver en el ejemplo siguiente.

Actualización de un ejemplo de interfaz de Java 8

 //Java interface
public interface Payable {
//abstract method
public abstract void paymentAmount();
//concrete method
public default void companyName() {
System.out.println("Software Company");
}
}

Antes de Java 8, agregar el método concreto en el código anterior a su interfaz rompería la interfaz. Esto se debe a que las interfaces anteriores a Java 8 no podían tener métodos concretos. Sin embargo, si el método fuera abstracto, la interfaz no se vería afectada. Pero las clases que lo implementaron antes de que se agregara el nuevo método se romperían.

Ahora, debido a Java 8, agregar métodos concretos a una interfaz no romperá las clases que ya se han implementado. Por lo tanto, los objetos creados a partir de cualquiera de las dos clases de implementación en este ejemplo de administración de cuentas pueden usar el método companyName () sin alterar su código existente.

Ejemplo de método predeterminado de interfaz de Java 8

 public class Main {
public static void main(String[] args) {
//Create a new employee pay report
Payable JanesPay = new Employee("Jane Doe", "mid-level");
//call the default method from the interface
JanesPay.companyName();
//calculate the employee's pay
JanesPay.paymentAmount();
}
}

El código anterior producirá lo siguiente en la consola:

 Software Company
Jane Doe pay for this month is: 20.0

Uso de la generalización para crear código reutilizable

Ahora puede usar operaciones similares para clases que no están relacionadas convencionalmente con la ayuda de Java 8 Interfaces. También tiene la opción y el conocimiento para utilizar métodos tanto concretos como abstractos en sus interfaces.

Pero para las clases que están relacionadas convencionalmente, puede aprender a reutilizar el código con herencia.