====== Interfaces funcionales, lambdas y referencias a métodos ====== ==== Interfaces funcionales ==== * Las interfaces funcionales son interfaces que poseen un único método abstracto (sin implementar) * Aparecen por primera vez en Java 8 * Están especialmente preparadas para usarse con expresiones lambda * Pueden tener definidos métodos por defecto (tienen implementación) En Java existen una serie de interfaces funcionales ya definidas especialmente útiles * **Predicate**: Evalua una condición booleana * //IntPredicate//, //LongPredicate//, //DoublePredicate// * **Consumer**: Acepta un input como entrada y no devuelve ningún resultado * //IntConsumer//, //LongConsumer//, //DoubleConsumer// * **Function**: Acepta un input como entrada y devuelve un resultado * //IntFunction//, //IntToDoubleFunction//, //IntToLongFunction//, //LongFunction//, LongToDoubleFunction, . . . * **Supplier**: No acepta argumentos y devuelve un resultado (al contrario que //Consumer//) * //BooleanSupplier//, //IntSupplier//, //LongSupplier//, //DoubleSupplier// * **BiPredicate**: Versión de //Predicat//e para dos argumentos * **BiFunction**: Versión de //Function// para dos argumentos Predicate checkEmpty = (value) -> value.isEmpty(); System.out.println(checkEmpty.test("hola")); System.out.println(checkEmpty.test("")); Pero también podemos crear las nuestras propias: @FunctionalInterface public interface SayMyName { void say(String name); } ==== Expresiones lambda ==== * Aparecen por primera vez en Java 8 * Código más compacto y limpio * Sustituyen al concepto de clase anónima * Permiten expresar las instancias de interfaces funcionales de una forma compacta * Su uso está especialmente extendido en el uso de Streams, donde se pueden utilizar de forma que el código queda mucho más compacto y fácil de entender * Cuando usamos una tenemos que imaginarnos que estamos implementando el código necesario para ejecutar luego el método que tiene por defecto asignada esa interface La sintaxis es: (parametros) -> (cuerpo-lambda) Por ejemplo: // Para ordenar una lista listaAnimales.sort((animal1, animal2) -> (animal1.getPeso() – animal2.getPeso()); // Para crear un Predicate que permita evaluar una colección sobre un objeto Animal unAnimal = new Animal(. . . .) Predicate pesoMayorQue10 = (animal) -> animal.getPeso() > 10; pesoMayorQue10.test(unAnimal); // true/false ==== Referencias a métodos ==== Suponiendo que tenemos una colección de cadenas, veamos unos ejemplos que nos permitan utilizar una referencia a un método: List cadenas = new ArrayList<>(); === Métodos estáticos === // Utilizando una expresión lambda cadenas.forEach((cadena) -> System.out.println(cadena)) // Equivalente utilizando referencia a método cadenas.forEach(System.out::println) === Métodos de instancia === // Suponemos que existe una clase CadenasComparator que implementa Comparator // Utilizando una expresión lambda cadenas.sort((cadena1, cadena2) -> cadenasComparator.compare(cadena1, cadena2)) // Equivalente utilizando una referencia a método suponiendo que existe un objeto de la clase que implementa a CadenasComparator cadenas.sort(cadenasComparator::compare); // Equivalente utilizando una referencia a método cadenas.sort(CadenasComparator::compare); === Constructores === CadenasComparator::new ---- (c) 2019-{{date>%Y}} Santiago Faci