Filtro de flujo de Java con expresión Lambda

1. Introducción

En este tutorial rápido, exploraremos el uso del método Stream.filter () cuando trabajamos con Streams en Java.

Veremos cómo usarlo y cómo manejar casos especiales con excepciones marcadas.

2. Usando Stream.filter ()

El método filter () es una operación intermedia de la interfaz de Stream que nos permite filtrar elementos de un stream que coinciden con un Predicado dado :

Stream filter(Predicate predicate)

Para ver cómo funciona esto, creemos una clase de cliente :

public class Customer { private String name; private int points; //Constructor and standard getters }

Además, creemos una colección de clientes:

Customer john = new Customer("John P.", 15); Customer sarah = new Customer("Sarah M.", 200); Customer charles = new Customer("Charles B.", 150); Customer mary = new Customer("Mary T.", 1); List customers = Arrays.asList(john, sarah, charles, mary);

2.1. Filtrar colecciones

Un caso de uso común del método filter () es procesar colecciones.

Hagamos una lista de clientes con más de 100 puntos. Para hacer eso, podemos usar una expresión lambda:

List customersWithMoreThan100Points = customers .stream() .filter(c -> c.getPoints() > 100) .collect(Collectors.toList());

También podemos usar una referencia de método, que es la abreviatura de una expresión lambda:

List customersWithMoreThan100Points = customers .stream() .filter(Customer::hasOverHundredPoints) .collect(Collectors.toList());

En este caso, agregamos el método hasOverHundredPoints a nuestra clase Customer :

public boolean hasOverHundredPoints() { return this.points > 100; }

En ambos casos, obtenemos el mismo resultado:

assertThat(customersWithMoreThan100Points).hasSize(2); assertThat(customersWithMoreThan100Points).contains(sarah, charles);

2.2. Filtrado de colecciones con varios criterios

Además, podemos usar múltiples condiciones con filter () . Por ejemplo, podemos filtrar por puntos y nombre :

List charlesWithMoreThan100Points = customers .stream() .filter(c -> c.getPoints() > 100 && c.getName().startsWith("Charles")) .collect(Collectors.toList()); assertThat(charlesWithMoreThan100Points).hasSize(1); assertThat(charlesWithMoreThan100Points).contains(charles);

3. Manejo de excepciones

Hasta ahora, hemos estado usando el filtro con predicados que no generan una excepción. De hecho, las interfaces funcionales en Java no declaran ninguna excepción marcada o no marcada .

A continuación, mostraremos algunas formas diferentes de manejar excepciones en expresiones lambda.

3.1. Usar una envoltura personalizada

Primero, comenzaremos agregando un profilePhotoUrl a nuestro Cliente :

private String profilePhotoUrl;

Además, agreguemos un método simple hasValidProfilePhoto () para verificar la disponibilidad del perfil:

public boolean hasValidProfilePhoto() throws IOException { URL url = new URL(this.profilePhotoUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); return connection.getResponseCode() == HttpURLConnection.HTTP_OK; }

Podemos ver que el método hasValidProfilePhoto () arroja una IOException . Ahora bien, si intentamos filtrar a los clientes con este método:

List customersWithValidProfilePhoto = customers .stream() .filter(Customer::hasValidProfilePhoto) .collect(Collectors.toList());

Veremos el siguiente error:

Incompatible thrown types java.io.IOException in functional expression

Para manejarlo, una de las alternativas que podemos utilizar es envolverlo con un bloque try-catch:

List customersWithValidProfilePhoto = customers .stream() .filter(c -> { try { return c.hasValidProfilePhoto(); } catch (IOException e) { //handle exception } return false; }) .collect(Collectors.toList());

Si necesitamos lanzar una excepción de nuestro predicado, podemos envolverla en una excepción sin marcar como RuntimeException .

3.2. Usando ThrowingFunction

Alternativamente, podemos usar la biblioteca ThrowingFunction.

ThrowingFunction es una biblioteca de código abierto que nos permite manejar excepciones marcadas en interfaces funcionales Java.

Comencemos agregando la dependencia de la función de lanzamiento a nuestro pom:

 pl.touk throwing-function 1.3 

Para manejar excepciones en predicados, esta biblioteca nos ofrece la clase ThrowingPredicate , que tiene el método unchecked () para ajustar las excepciones marcadas.

Veámoslo en acción:

List customersWithValidProfilePhoto = customers .stream() .filter(ThrowingPredicate.unchecked(Customer::hasValidProfilePhoto)) .collect(Collectors.toList());

4. Conclusión

En este artículo, vimos un ejemplo de cómo usar el método filter () para procesar flujos. También exploramos algunas alternativas para manejar excepciones.

Como siempre, el código completo está disponible en GitHub.