Saltar al contenido

Java 8: Lambda-Streams, filtro por método con excepción

Solución:

Debes atrapar la excepción antes de se escapa de la lambda:

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

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

Puede manejarlo usando un contenedor de su lambda que traduce las excepciones marcadas a las no marcadas:

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

Tu ejemplo se escribiría como

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

En mis proyectos me ocupo de este tema sin envolver; en su lugar, utilizo un método que desactiva eficazmente 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 puede aparecer una excepción marcada donde no se declara. Este es el código de plomería:

public static  T uncheckCall(Callable 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  void sneakyThrow(Throwable t) throws T 
    throw (T) t;

y puedes esperar obtener un IOException arrojado en tu cara, aunque collect no lo declara. En la mayoría, pero no todos En los casos de la vida real, querría volver a generar la excepción, de todos modos, y manejarla como una falla genérica. En todos esos casos, nada se pierde en claridad o corrección. Solo tenga cuidado con esos otros casos, en los que realmente querría reaccionar a la excepción en el acto. El compilador no informará al desarrollador de que existe un IOException para atraparlo allí y el compilador de hecho se quejará si intenta atraparlo porque lo hemos engañado haciéndole creer que no se puede lanzar tal excepción.

También puede propagar su static dolor 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 detectada durante la llamada en RuntimeException. Existe un método de conversión similar Throwables # propagate (Throwable) en Guava.

Este método parece ser esencial para el encadenamiento de métodos lambda, por lo que espero que algún día se agregue a una de las bibliotecas populares o este comportamiento de propagación sería 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 propagate(Callable 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 accounts = new ArrayList<>(Arrays.asList(new Account("andrey"), new Account("angela"), new Account("pamela")));

        Stream s = accounts.stream();

        s
          .filter(a -> propagate(a::isActive))
          .map(a -> a.name)
          .forEach(System.out::println);
    

Esta UtilException La clase de ayuda le permite usar cualquier excepción marcada en las transmisiones de 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, cual es comprobado. La propia corriente también arroja ClassNotFoundException, y NO alguna excepción de envoltura sin marcar.

public final class UtilException 

@FunctionalInterface
public interface Consumer_WithExceptions 
    void accept(T t) throws E;
    

@FunctionalInterface
public interface BiConsumer_WithExceptions 
    void accept(T t, U u) throws E;
    

@FunctionalInterface
public interface Function_WithExceptions 
    R apply(T t) throws E;
    

@FunctionalInterface
public interface Supplier_WithExceptions 
    T get() throws E;
    

@FunctionalInterface
public interface Runnable_WithExceptions 
    void run() throws E;
    

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

public static  BiConsumer rethrowBiConsumer(BiConsumer_WithExceptions 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  Function rethrowFunction(Function_WithExceptions 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  Supplier rethrowSupplier(Supplier_WithExceptions 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 uncheck(Supplier_WithExceptions supplier)
    
    try  return supplier.get(); 
    catch (Exception exception)  throwAsUnchecked(exception); return null; 
    

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

@SuppressWarnings ("unchecked")
private static  void throwAsUnchecked(Exception exception) throws E  throw (E)exception; 


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

@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 classes1
          = Stream.of("Object", "Integer", "String")
                  .map(rethrowFunction(className -> Class.forName("java.lang." + className)))
                  .collect(Collectors.toList());

    List 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 código de llamada es para manejar la excepción marcada, DEBE agregarlo a la cláusula throws del método que contiene la secuencia. El compilador ya no lo obligará a agregarlo, 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 el flujo (si no lo hace, dirá: La excepción nunca se lanza en el cuerpo de la declaración try correspondiente ).

• En cualquier caso, no podrá rodear la transmisión para detectar la excepción marcada DENTRO del método que contiene la transmisión (si lo intenta, el compilador dirá: La excepción nunca se incluye en el cuerpo de la declaració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: new String (byteArr, “UTF-8”) arroja UnsupportedEncodingException, pero la especificación de Java garantiza que UTF-8 siempre estará presente. Aquí, la declaración de lanzamientos es una molestia y cualquier solución para silenciarla con un mínimo de repetición es bienvenida.

• Si odia las excepciones marcadas y cree que nunca deberían agregarse al lenguaje Java para empezar (un número creciente de personas piensa de esta manera, y yo NO soy una de ellas), entonces no agregue la excepción marcada a la throws cláusula del método que contiene la secuencia. La excepción marcada, entonces, se comportará como una excepción no marcada.

• Si está implementando una interfaz estricta en la que no tiene la opción de agregar una declaración throws y, sin embargo, lanzar una excepción es totalmente apropiado, entonces empaquetar una excepción solo para obtener el privilegio de lanzarla da como resultado un seguimiento de pila con excepciones falsas que no aportan información sobre lo que realmente salió mal. Un buen ejemplo es Runnable.run (), que no arroja ninguna excepción marcada. En este caso, puede decidir no agregar 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 CHECKED:

1) El código de llamada no podrá capturarlo por su nombre (si lo intenta, el compilador dirá: La excepción nunca se incluye en el cuerpo de la declaración try correspondiente). Burbujeará y probablemente será atrapado en el bucle principal del programa por alguna “excepción de captura” o “captura Throwable”, que puede ser lo que desea de todos modos.

2) Viola el principio de la mínima sorpresa: ya no será suficiente capturar RuntimeException para poder garantizar la captura de todas las posibles excepciones. Por esta razón, creo que esto no debería hacerse en un código marco, sino solo en un código comercial que usted controla por completo.

En conclusión: creo que las limitaciones aquí no son serias, y la UtilException La clase se puede utilizar sin miedo. Sin embargo, ¡depende de ti!

  • Referencias:
    • http://www.philandstuff.com/2012/04/28/sneakily-throwing-checked-exceptions.html
    • http://www.mail-archive.com/[email protected]/msg05984.html
    • Anotación del Proyecto Lombok: @SneakyThrows
    • Opinión de Brian Goetz (en contra) aquí: ¿Cómo puedo lanzar excepciones CHECKED desde dentro de las transmisiones de Java 8?
    • https://softwareengineering.stackexchange.com/questions/225931/workaround-for-java-checked-exceptions?newreg=ddf0dd15e8174af8ba52e091cf85688e *
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *