En java 8, el lenguaje ha añadido la habilidad de escribir código en otro estilo: Programación Funcional. La Programación Funcional es más declarativa y usa expresiones Lambda para escribir código. Una expresión Lambda es un bloque de código que se envía a un método.

Las expresiones Lambda son a menudo conocidas  como “Closures” en otros lenguajes. Este tipo de expresiones, lambda a partir de ahora, son expresiones que pasas como una variable en un método. Vamos a hacer un pequeño ejemplo:

Creamos una clase Animal muy sencilla:
package org.nemedus.entities;

public class Animal {

    private String species;
    private boolean canHop;
    private boolean canSwim;

    public Animal(String species, boolean canHop, boolean canSwim) {
        this.species = species;
        this.canHop = canHop;
        this.canSwim = canSwim;
    }

    public boolean canHop() {
        return canHop;
    }

    public boolean canSwim() {
        return canSwim;
    }

    public String toString() {
        return species;
    }
}
Luego vamos a crear un interfaz para las acciones:
package org.nemedus.actions;

import org.nemedus.entities.Animal;

public interface CheckTrait {
 boolean test (Animal a);
}
Ahora creamos un par de clases para acciones:
package org.nemedus.actions;

import org.nemedus.entities.Animal;

public class CheckIfHopper implements CheckTrait {
    public boolean test (Animal a) {
        return a.canHop();
    }
}
package org.nemedus.actions;

import org.nemedus.entities.Animal;

public class CheckifSwim implements CheckTrait {
    public boolean test (Animal a) {
        return a.canSwim();
    }
}
Y finalmente una clase que implemente todo:
public class SimpleLambdas {
    public static void main (String[] args) {
        System.out.println("hola que haces?");

        List<Animal> animals = new ArrayList<>();
        animals.add(new Animal("fish", false, true));
        animals.add(new Animal("kangaroo", true, false));
        animals.add(new Animal("rabbit", true, false));
        animals.add(new Animal("turtle", false, true));

        print(animals, new CheckIfHopper());
        print(animals, new CheckifSwim());
    }

    private static void print(List<Animal> animals, CheckTrait checker) {
        for (Animal animal: animals) {
            if (checker.test(animal)) {
                System.out.println(animal + "");
            }
        }
        System.out.println();
    }
}

Viendo este ejemplo, podemos ver que si queremos añadir una nueva acción a Animal, tendríamos que crear una nueva clase, que implementara el interfaz “CheckTrait” y añadir una nueva linea de código en la clase “SimpleLambdas”.  Pero si usamos las lambdas…solo tendríamos que añadir una línea de código:

print(animals, a -> a.canSwim());

Lambda Syntax

La expresión más simple de una lambda que podemos encontrar es la siguiente: “a -> a.canHop()”, este método devuelve un boolean, nosotros lo sabemos porque hemos escrito el código, pero ¿como lo sabe Java?.

Java replica el contexto cuando se da cuenta del significado de una expresión lambda. Pasamos la expresión lambda como segundo parámetro del método print() y este método espera a CheckTrait como segundo parámetro.  Cuando pasamos un lambda, Java intenta mapear nuestro interfaz Lambda a “boolean test (Animal a)”. Como el método tiene como parámetro un objeto del tipo Animal, el segundo parámetro tiene que ser un Animal y como devuelve un boleano, el valor de vuelta es un boleano también.

Estas dos líneas significan lo mismo:

a -> a.canHop()
(Animal a) -> {return a.canHop();}

Tenemos que tener en cuenta a la hora de usar lambdas:

  • Los paréntesis solo pueden omitirse si solo hay un parámetro y su tipo no tiene un estado explícito.
  • Podemos omitir los “corchetes” si solo vamos a tener una instrucción.
  • Si vamos a ejecutar más de una instrucción debemos usar los corchetes, añadir un return y no olvidar “;”.
  • No se permite re declarar una variable en una lambda.
  • Lambda pueden acceder a variables de instancia o estáticas.
  • También puede acceder a parámetros de un método y variables locales si no están asignados a nuevas valores.
 print(animals, a -> a.canSwim());
 print(animals, a -> a.canHop());

Predicados

Las lambdas funciona con interfaces que solo tienen un método, a estos los llamamos interfaces funcionales. (Son interfaces que se pueden usar con la programación funcional, realmente es más complejo, pero esto es una simplificación).

Si queremos tener más objetos que implementen el mismo comportamiento, deberemos tener un interfaz de este tipo cada objeto, pero por suerte, Java implementa un tipo de interfaz para nosotros. Esta en el paquete “java.util.funcion” y es de la siguiente forma:

public interface Predicate<T> {
    boolean test(T t);
}

Es igual que nuestro interfaz solo que incorpora el tipo <T>, como cuando usamos un ArrayList. Java 8 integra el interfaz Predicado en algunas clases. Como por ejemplo en la clase ArryList, en el metodo removeIf recibe un predicado. Suponiendo que tengamos un array de Nombre, podríamos borrar todos aquellos que comienzan por E.

List<String> names = new ArrayList<>();
names.add("Antonio");
names.add("Carmen");
names.add("Enrique");
names.add("Maria");
        
names.removeIf(a -> a.charAt(0) == 'E');
        
for (String name: names) {
     System.out.println(name);
}

Descárgate el código

Last modified: 08/08/2020