Interfaces funcionales
Se llaman interfaces funcionales a aquellas que tienen un solo método abstracto que implementar (puede haber más métodos pero ya con una implementación por defecto). Son especialmente importantes porque tienen una sintaxis alternativa que permite una implementación más simple. Esto hace que existan muchas interfaces para tareas específicas que aparecen con frecuencia en el trabajo del programador. Por ejemplo, la interfaz Comparator.
Existen tres formas de implementar una interfaz funcional.
Para todos los ejemplos, supongamos la siguiente clase Cliente:
public class Cliente {
private String dni;
private String nombre;
private int edad;
public Cliente(String dni, String nombre, int edad) {
this.dni = dni;
this.nombre = nombre;
this.edad = edad;
}
public String getDni() {
return dni;
}
public String getNombre() {
return nombre;
}
public int getEdad() {
return edad;
}
}
Crear explícitamente una clase
Podemos crear explícitamente una clase ComparaNombres, que implemente la interfaz Comparator, que compare objetos cliente basándose en el atributo nombre que es un String.
public class ComparaNombres implements Comparator<Cliente> {
public int compare(Cliente c1, Cliente c2) {
return c1.getNombre().compareTo(c2.getNombre());
}
}
A continuación creamos un objeto de ComparaNombres y se lo pasamos a la función donde se va a usar:
Collections.sort(lista, new ComparaNombres()); // La lista queda ordenada por nombres
Crear un objeto con una clase anónima
Si vamos a usar el comparador una sola vez, no merece la pena implementar la clase comparadora explícitamente. Podemos crear una clase anónima:
Collections.sort(lista, new Comparator<Cliente>(){
public int compare(Cliente c1, Cliente c2) {
return c1.getNombre().compareTo(c2.getNombre());
}
});
Utilizando expresiones lambda
La forma más rápida de implementar una interfaz funcional es utilizar expresiones lambda. Para esto, basta escribir la lista de parámetros y el cuerpo de la función abstracta separados con una flecha (->). Por ejemplo, vamos a implementar el método compare() de la interfaz Comparator:
Comparator<Cliente> comp = (Cliente c1, Cliente c2) -> {
return c1.getNombre().compareTo(c2.getNombre());
};
Todo lo que está tras la flecha y entre llaves después de la flecha es el cuerpo del método compare(). No es necesario indicar el nombre del método porque la interfaz Comparator solo tiene un único método.
También se puede inferir el tipo de datos de los parámetros de entrada, por lo tanto se puede omitir en el lado derecho porque ya se indica en el lado izquierdo como el tipo de la interfaz Comparator:
Comparator<Cliente> comp = (c1,c2) -> {
return c1.getNombre().compareTo(c2.getNombre());
};
Dentro de las llaves tras la flecha se pueden incluir cualquier número de sentencias.
Si únicamente tenemos una única sentencia, podemos omitir las llaves:
Comparator<Cliente> comp = (c1,c2) -> return c1.getNombre().compareTo(c2.getNombre());
Incluso podemos prescindir del return porque en todas las expresiones lambda se devuelve la última sentencia.
Comparator<Cliente> comp = (c1,c2) -> c1.getNombre().compareTo(c2.getNombre());
Además, podemos prescindir de la variable comp y definir la expresión lambda dentro del parámetro del método sort():
Collections.sort(lista, (c1,c2) -> c1.getNombre().compareTo(c2.getNombre()));