Java 8: Lambda-Streams, Filtrar por Método con Excepción


Tengo un problema al probar las expresiones Lambda de Java 8. Normalmente funciona bien, pero ahora tengo métodos que lanzan IOException's. Es mejor si nos fijamos en el siguiente código:

class Bank{
    ....
    public Set<String> getActiveAccountNumbers() throws IOException {
        Stream<Account> s =  accounts.values().stream();
        s = s.filter(a -> a.isActive());
        Stream<String> ss = s.map(a -> a.getNumber());
        return ss.collect(Collectors.toSet());
    }
    ....
}

interface Account{
    ....
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
    ....
}

El problema es que no compila, porque tengo que captar las posibles excepciones de los métodos isActive - y getNumber -. Pero incluso si uso explícitamente un try-catch-Block como el de abajo, todavía no compila porque no cojo la Excepción. Así que o hay un error en JDK, o no lo sé cómo captar estas excepciones.

class Bank{
    ....
    //Doesn't compile either
    public Set<String> getActiveAccountNumbers() throws IOException {
        try{
            Stream<Account> s =  accounts.values().stream();
            s = s.filter(a -> a.isActive());
            Stream<String> ss = s.map(a -> a.getNumber());
            return ss.collect(Collectors.toSet());
        }catch(IOException ex){
        }
    }
    ....
}

¿Cómo puedo hacer que funcione? ¿Alguien puede sugerirme la solución correcta?

Author: Jack, 2013-11-03

12 answers

Debes atrapar la excepción antes de que se escape de la lambda:

s = s.filter(a -> { try { return a.isActive(); } 
                    catch (IOException e) { throw new UncheckedIOException(e); }}});

Considere el hecho de que la lambda no se evalúa en el lugar donde la escribe, sino en algún lugar completamente no relacionado, dentro de una clase JDK. Así que ese sería el punto donde se lanzaría esa excepción marcada, y en ese lugar no se declara.

Puede lidiar con ello usando un envoltorio de su lambda que traduce las excepciones marcadas a las no marcadas:

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (RuntimeException e) { throw e; }
  catch (Exception e) { throw new RuntimeException(e); }
}

Su ejemplo se escribiría como

return s.filter(a -> uncheckCall(a::isActive))
        .map(Account::getNumber)
        .collect(toSet());

En mis proyectos trato con este problema sin envolver; en su lugar, uso un método que desactiva efectivamente la comprobación de excepciones del compilador. No hace falta decir que esto debe manejarse con cuidado y todos en el proyecto deben ser conscientes de que una excepción marcada puede aparecer cuando no se declara. Este es el código de fontanería:

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (Exception e) { return sneakyThrow(e); }
}
public static void uncheckRun(RunnableExc r) {
  try { r.run(); } catch (Exception e) { sneakyThrow(e); }
}
public interface RunnableExc { void run() throws Exception; }


@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
  throw (T) t;
}

Y puedes esperar que te arrojen un IOException en la cara, aunque collect no lo declare. En la mayoría, pero no todos los casos de la vida real que le gustaría simplemente repensar la excepción, de todos modos, y manejarlo como un fallo genérico. En todos esos casos, nada se pierde en claridad o corrección. Solo ten cuidado con esos otros casos, donde realmente querrías reaccionar a la excepción en el acto. El desarrollador no se dará cuenta por el compilador que hay un IOException para atrapar allí y el compilador de hecho se quejará si intenta atraparlo porque lo hemos engañado para creer que no hay tal se puede lanzar una excepción.

 180
Author: Marko Topolnik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-01 13:02:55

También puede propagar su dolor estático con lambdas, por lo que todo parece legible:

s.filter(a -> propagate(a::isActive))

propagate aquí recibe java.util.concurrent.Callable como parámetro y convierte cualquier excepción capturada durante la llamada en RuntimeException. Hay un método de conversión similar Throwables#propagate(Throwable) en Guayaba.

Este método parece ser esencial para el encadenamiento del método lambda, así que espero que algún día se agregue a una de las libs populares o este comportamiento de propagación sería por predeterminado.

public class PropagateExceptionsSample {
    // a simplified version of Throwables#propagate
    public static RuntimeException runtime(Throwable e) {
        if (e instanceof RuntimeException) {
            return (RuntimeException)e;
        }

        return new RuntimeException(e);
    }

    // this is a new one, n/a in public libs
    // Callable just suits as a functional interface in JDK throwing Exception 
    public static <V> V propagate(Callable<V> callable){
        try {
            return callable.call();
        } catch (Exception e) {
            throw runtime(e);
        }
    }

    public static void main(String[] args) {
        class Account{
            String name;    
            Account(String name) { this.name = name;}

            public boolean isActive() throws IOException {
                return name.startsWith("a");
            }
        }


        List<Account> accounts = new ArrayList<>(Arrays.asList(new Account("andrey"), new Account("angela"), new Account("pamela")));

        Stream<Account> s = accounts.stream();

        s
          .filter(a -> propagate(a::isActive))
          .map(a -> a.name)
          .forEach(System.out::println);
    }
}
 23
Author: Andrey Chaschev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-03 23:53:32

Esta clase helper UtilException le permite usar cualquier excepción marcada en secuencias Java, como esta:

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
      .map(rethrowFunction(Class::forName))
      .collect(Collectors.toList());

Nota Class::forName lanza ClassNotFoundException, que es comprobado. La secuencia en sí también lanza ClassNotFoundException, y NO alguna excepción de envoltura sin marcar.

public final class UtilException {

@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
    void accept(T t) throws E;
    }

@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
    void accept(T t, U u) throws E;
    }

@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
    R apply(T t) throws E;
    }

@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
    T get() throws E;
    }

@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
    void run() throws E;
    }

/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
    return t -> {
        try { consumer.accept(t); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
    return (t, u) -> {
        try { biConsumer.accept(t, u); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
    return t -> {
        try { return function.apply(t); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
    return () -> {
        try { return function.get(); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
    {
    try { t.run(); }
    catch (Exception exception) { throwAsUnchecked(exception); }
    }

/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
    {
    try { return supplier.get(); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
    try { return function.apply(t); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }

}

Muchos otros ejemplos sobre cómo usarlo (después de importar estáticamenteUtilException):

@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(System.out::println));
    }

@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
    List<Class> classes1
          = Stream.of("Object", "Integer", "String")
                  .map(rethrowFunction(className -> Class.forName("java.lang." + className)))
                  .collect(Collectors.toList());

    List<Class> classes2
          = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
                  .map(rethrowFunction(Class::forName))
                  .collect(Collectors.toList());
    }

@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
    Collector.of(
          rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
          StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
    }

@Test    
public void test_uncheck_exception_thrown_by_method() {
    Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));

    Class clazz2 = uncheck(Class::forName, "java.lang.String");
    }

@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
    Class clazz3 = uncheck(Class::forName, "INVALID");
    }

Pero no lo use antes de comprender las siguientes ventajas, desventajas y limitaciones :

• Si el calling-code es para manejar la excepción marcada, debe agregarla a la cláusula throws del método que contiene la secuencia. El compilador no te obligará a añadirlo más, por lo que es más fácil olvidarlo.

* Si el código de llamada ya maneja la excepción marcada, el compilador le recordará que agregue la cláusula throws a la declaración del método que contiene la secuencia (si no lo hace, dirá: Exception nunca se arroja en el cuerpo de la instrucción try correspondiente).

• En en cualquier caso, no podrá rodear la secuencia en sí para capturar la excepción marcada DENTRO del método que contiene la secuencia (si intenta, el compilador dirá: Exception nunca se arroja en el cuerpo de la instrucción try correspondiente).

• Si está llamando a un método que literalmente nunca puede lanzar la excepción que declara, entonces no debe incluir la cláusula throws. Por ejemplo: una nueva cadena (byteArr, "UTF-8") lanza una excepción no soportada de ENCODING, pero UTF-8 está garantizada por la especificación de Java para estar siempre presente. Aquí, la declaración de throws es una molestia y cualquier solución para silenciarla con un mínimo repetitivo es bienvenida.

* Si odia las excepciones verificadas y siente que nunca deben agregarse al lenguaje Java para empezar (un número creciente de personas piensa de esta manera, y YO NO soy uno de ellos), entonces simplemente no agregue la excepción marcada a la cláusula throws del método que contiene la secuencia. El comprobado la excepción, entonces, se comportará como un Excepción sin marcar.

* Si está implementando una interfaz estricta donde no tiene la opción de agregar una declaración throws, y sin embargo lanzar una excepción es completamente apropiado, luego envolver una excepción solo para obtener el privilegio de lanzarla resulta en una carrera de apilamiento con excepciones espurias que no aporte información sobre lo que realmente salió mal. Un buen ejemplo es ejecutable.run (), que no arroja ninguna excepción marcada. En este caso, puede decidir no hacerlo agregue la excepción marcada a la cláusula throws del método que contiene la secuencia.

* En cualquier caso, si decide NO agregar (u olvida agregar) la excepción marcada a la cláusula throws del método que contiene la secuencia, tenga en cuenta estas 2 consecuencias de lanzar excepciones COMPROBADAS:

1) El código de llamada no podrá capturarlo por su nombre (si lo intenta, el compilador dirá: Exception nunca se arroja en el cuerpo del intento correspondiente instrucción). Burbujeará y probablemente sea atrapado en el bucle principal del programa por alguna "excepción de captura" o "captura lanzable", que puede ser lo que quiero de todos modos.

2) Viola el principio de menor sorpresa: ya no será suficiente atrapar RuntimeException para poder garantizar la captura de todos posibles excepciones. Por esta razón, creo que esto no debe hacerse en código marco, sino solo en código de negocios que usted controle completamente.

En conclusión: Creo que las limitaciones aquí no son grave, y la clase UtilException se puede utilizar sin miedo. Sin embargo, depende de usted!

 17
Author: MarcG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-26 19:16:31

Usted puede potencialmente rodar su propia variante Stream envolviendo su lambda para lanzar una excepción sin marcar y luego desenvolver esa excepción sin marcar en operaciones de terminal:

@FunctionalInterface
public interface ThrowingPredicate<T, X extends Throwable> {
    public boolean test(T t) throws X;
}

@FunctionalInterface
public interface ThrowingFunction<T, R, X extends Throwable> {
    public R apply(T t) throws X;
}

@FunctionalInterface
public interface ThrowingSupplier<R, X extends Throwable> {
    public R get() throws X;
}

public interface ThrowingStream<T, X extends Throwable> {
    public ThrowingStream<T, X> filter(
            ThrowingPredicate<? super T, ? extends X> predicate);

    public <R> ThrowingStream<T, R> map(
            ThrowingFunction<? super T, ? extends R, ? extends X> mapper);

    public <A, R> R collect(Collector<? super T, A, R> collector) throws X;

    // etc
}

class StreamAdapter<T, X extends Throwable> implements ThrowingStream<T, X> {
    private static class AdapterException extends RuntimeException {
        public AdapterException(Throwable cause) {
            super(cause);
        }
    }

    private final Stream<T> delegate;
    private final Class<X> x;

    StreamAdapter(Stream<T> delegate, Class<X> x) {
        this.delegate = delegate;
        this.x = x;
    }

    private <R> R maskException(ThrowingSupplier<R, X> method) {
        try {
            return method.get();
        } catch (Throwable t) {
            if (x.isInstance(t)) {
                throw new AdapterException(t);
            } else {
                throw t;
            }
        }
    }

    @Override
    public ThrowingStream<T, X> filter(ThrowingPredicate<T, X> predicate) {
        return new StreamAdapter<>(
                delegate.filter(t -> maskException(() -> predicate.test(t))), x);
    }

    @Override
    public <R> ThrowingStream<R, X> map(ThrowingFunction<T, R, X> mapper) {
        return new StreamAdapter<>(
                delegate.map(t -> maskException(() -> mapper.apply(t))), x);
    }

    private <R> R unmaskException(Supplier<R> method) throws X {
        try {
            return method.get();
        } catch (AdapterException e) {
            throw x.cast(e.getCause());
        }
    }

    @Override
    public <A, R> R collect(Collector<T, A, R> collector) throws X {
        return unmaskException(() -> delegate.collect(collector));
    }
}

Entonces podrías usar esto exactamente de la misma manera que un Stream:

Stream<Account> s = accounts.values().stream();
ThrowingStream<Account, IOException> ts = new StreamAdapter<>(s, IOException.class);
return ts.filter(Account::isActive).map(Account::getNumber).collect(toSet());

Esta solución requeriría un poco de repetición, así que le sugiero que eche un vistazo a la biblioteca que ya hice que hace exactamente lo que he descrito aquí para toda la clase Stream (¡y más!).

 8
Author: Jeffrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-26 04:02:55

Use el método #propagate (). Ejemplo de implementación no guayaba de Java 8 Blog de Sam Beran :

public class Throwables {
    public interface ExceptionWrapper<E> {
        E wrap(Exception e);
    }

    public static <T> T propagate(Callable<T> callable) throws RuntimeException {
        return propagate(callable, RuntimeException::new);
    }

    public static <T, E extends Throwable> T propagate(Callable<T> callable, ExceptionWrapper<E> wrapper) throws E {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw wrapper.wrap(e);
        }
    }
}
 5
Author: n0mer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-24 12:05:03

Se puede resolver por debajo del código simple con Stream y Try in AbacusUtil :

Stream.of(accounts).filter(a -> Try.call(a::isActive)).map(a -> Try.call(a::getNumber)).toSet();

Divulgación: soy el desarrollador de AbacusUtil.

 3
Author: user_3380739,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-07 18:03:03

Para agregar correctamente el código de manejo de IOException (a RuntimeException), su método se verá así:

Stream<Account> s =  accounts.values().stream();

s = s.filter(a -> { try { return a.isActive(); } 
  catch (IOException e) { throw new RuntimeException(e); }});

Stream<String> ss = s.map(a -> { try { return a.getNumber() }
  catch (IOException e) { throw new RuntimeException(e); }});

return ss.collect(Collectors.toSet());

El problema ahora es que el IOException tendrá que ser capturado como un RuntimeException y convertido de nuevo a un IOException} y eso agregará aún más código al método anterior.

Por qué usar Stream cuando se puede hacer así just y el método arroja IOException así que no se necesita código adicional para eso también:

Set<String> set = new HashSet<>();
for(Account a: accounts.values()){
  if(a.isActive()){
     set.add(a.getNumber());
  } 
}
return set;
 3
Author: Austin Powers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-05 06:59:13

Extendiendo la solución @marcg, normalmente puede lanzar y atrapar una excepción marcadaen Flujos; es decir, el compilador le pedirá que atrape/vuelva a lanzar como si estuviera fuera de los flujos!!

@FunctionalInterface
public interface Predicate_WithExceptions<T, E extends Exception> {
    boolean test(T t) throws E;
}

/**
 * .filter(rethrowPredicate(t -> t.isActive()))
 */
public static <T, E extends Exception> Predicate<T> rethrowPredicate(Predicate_WithExceptions<T, E> predicate) throws E {
    return t -> {
        try {
            return predicate.test(t);
        } catch (Exception exception) {
            return throwActualException(exception);
        }
    };
}

@SuppressWarnings("unchecked")
private static <T, E extends Exception> T throwActualException(Exception exception) throws E {
    throw (E) exception;
}

Entonces, su ejemplo se escribiría de la siguiente manera (agregando pruebas para mostrarlo más claramente):

@Test
public void testPredicate() throws MyTestException {
    List<String> nonEmptyStrings = Stream.of("ciao", "")
            .filter(rethrowPredicate(s -> notEmpty(s)))
            .collect(toList());
    assertEquals(1, nonEmptyStrings.size());
    assertEquals("ciao", nonEmptyStrings.get(0));
}

private class MyTestException extends Exception { }

private boolean notEmpty(String value) throws MyTestException {
    if(value==null) {
        throw new MyTestException();
    }
    return !value.isEmpty();
}

@Test
public void testPredicateRaisingException() throws MyTestException {
    try {
        Stream.of("ciao", null)
                .filter(rethrowPredicate(s -> notEmpty(s)))
                .collect(toList());
        fail();
    } catch (MyTestException e) {
        //OK
    }
}
 2
Author: PaoloC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-06 08:29:46

Esto no responde directamente a la pregunta (hay muchas otras respuestas que sí lo hacen), pero trata de evitar el problema en primer lugar:

En mi experiencia, la necesidad de manejar excepciones en una Stream (u otra expresión lambda) a menudo proviene del hecho de que las excepciones se declaran para ser lanzadas desde métodos donde no deben ser lanzadas. Esto a menudo proviene de la mezcla de lógica de negocios con entrada y salida. Su interfaz Account es un ejemplo perfecto:

interface Account {
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
}

En lugar de lanzando un IOException en cada getter, considere este diseño:

interface AccountReader {
    Account readAccount(…) throws IOException;
}

interface Account {
    boolean isActive();
    String getNumber();
}

El método AccountReader.readAccount(…) podría leer una cuenta de una base de datos o un archivo o lo que sea y lanzar una excepción si no tiene éxito. Construye un objeto Account que ya contiene todos los valores, listo para ser utilizado. Como los valores ya han sido cargados por readAccount(…), los getters no lanzarían una excepción. Por lo tanto, puede usarlos libremente en lambdas sin la necesidad de envolver, enmascarar u ocultar las excepciones.

De por supuesto, no siempre es posible hacerlo de la manera que describí, pero a menudo lo es y conduce a un código más limpio por completo (EN mi humilde opinión):

  • Mejor separación de preocupaciones y siguiendo principio de responsabilidad única
  • Menos repeticiones: No tienes que saturar tu código con throws IOException para nada más que para satisfacer al compilador
  • Manejo de errores: Usted maneja los errores donde ocurren - al leer desde un archivo o base de datos - en lugar de en algún lugar en la mitad de su lógica de negocio solo porque desea obtener un valor de campos
  • Usted puede ser capaz de hacer Account inmutable y beneficiarse de sus ventajas (por ejemplo, seguridad del hilo)
  • No necesitas "trucos sucios" o soluciones para usar Account en lambdas (por ejemplo, en un Stream)
 2
Author: siegi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-02 09:35:10

Teniendo en cuenta este problema, desarrollé una pequeña biblioteca para tratar con las excepciones verificadas y lambdas. Los adaptadores personalizados le permiten integrarse con tipos funcionales existentes:

stream().map(unchecked(URI::new)) //with a static import

Https://github.com/TouK/ThrowingFunction /

 1
Author: Grzegorz Piwowarek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-19 18:22:41

Su ejemplo puede ser escrito como:

import utils.stream.Unthrow;

class Bank{
   ....
   public Set<String> getActiveAccountNumbers() {
       return accounts.values().stream()
           .filter(a -> Unthrow.wrap(() -> a.isActive()))
           .map(a -> Unthrow.wrap(() -> a.getNumber()))
           .collect(Collectors.toSet());
   }
   ....
}

La clase Unthrow se puede tomar aquí https://github.com/SeregaLBN/StreamUnthrower

 1
Author: SeregaLBN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-04 13:18:01

Si no te importa usar bibliotecas de terceros, la lib de AOL cyclops-react, disclosure::I am a contributor, tiene una clase ExceptionSoftener que puede ayudar aquí.

 s.filter(softenPredicate(a->a.isActive()));
 0
Author: John McClean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-24 17:36:20