¿La función Java 8 Lambda arroja una excepción?

Sé cómo crear una referencia a un método que tiene un parámetro String y devuelve un int , es:

 Function 

Sin embargo, esto no funciona si la función arroja una excepción, digamos que se define como:

 Integer myMethod(String s) throws IOException 

¿Cómo definiría esta referencia?

Tendrá que hacer una de las siguientes cosas.

  • Si es su código, defina su propia interfaz funcional que declara la excepción marcada

     @FunctionalInterface public interface CheckedFunction { R apply(T t) throws IOException; } 

    y usarlo

     void foo (CheckedFunction f) { ... } 
  • De lo contrario, Integer myMethod(String s) en un método que no declare una excepción marcada:

     public Integer myWrappedMethod(String s) { try { return myMethod(s); } catch(IOException e) { throw new UncheckedIOException(e); } } 

    y entonces

     Function f = (String t) -> myWrappedMethod(t); 

    o

     Function f = (String t) -> { try { return myMethod(t); } catch(IOException e) { throw new UncheckedIOException(e); } }; 

En realidad, puede extender Consumer (y Function etc.) con una nueva interfaz que maneja excepciones, ¡utilizando los métodos por defecto de Java 8!

Considere esta interfaz (extiende al Consumer ):

 @FunctionalInterface public interface ThrowingConsumer extends Consumer { @Override default void accept(final T elem) { try { acceptThrows(elem); } catch (final Exception e) { // Implement your own exception handling logic here.. // For example: System.out.println("handling an exception..."); // Or ... throw new RuntimeException(e); } } void acceptThrows(T elem) throws Exception; } 

Entonces, por ejemplo, si tiene una lista:

 final List list = Arrays.asList("A", "B", "C"); 

Si desea consumirlo (por ejemplo, con forEach ) con algún código que arroje excepciones, tradicionalmente habría configurado un bloque try / catch:

 final Consumer consumer = aps -> { try { // maybe some other code here... throw new Exception("asdas"); } catch (final Exception ex) { System.out.println("handling an exception..."); } }; list.forEach(consumer); 

Pero con esta nueva interfaz, puede crear una instancia con una expresión lambda y el comstackdor no se quejará:

 final ThrowingConsumer throwingConsumer = aps -> { // maybe some other code here... throw new Exception("asdas"); }; list.forEach(throwingConsumer); 

¡O incluso solo lo lanzaste para que sea más sucinto !:

 list.forEach((ThrowingConsumer) aps -> { // maybe some other code here... throw new Exception("asda"); }); 

Actualización : Parece que hay una muy buena biblioteca de utilidad llamada Durian llamada Errores que se puede usar para resolver este problema con mucha más flexibilidad. Por ejemplo, en mi implementación anterior he definido explícitamente la política de manejo de errores ( System.out... o throw RuntimeException ), mientras que los errores de Durian le permiten aplicar una política sobre la marcha a través de un amplio conjunto de métodos de utilidad. Gracias por compartirlo , @NedTwigg !.

Uso de muestra:

 list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c))); 

Creo que la clase Errors de Durian combina muchos de los pros de las diversas sugerencias anteriores.

  • Envuelva una función de lanzamiento a una interfaz funcional Java 8 estándar.
  • Especifique fácilmente varias políticas para el manejo de errores
  • Al ajustar un método que devuelve un valor, hay una distinción importante entre especificar un valor predeterminado o volver a lanzar una RuntimeException.
  • Lanzamiento de versiones de las interfaces funcionales de Java 8
    • Similar a la respuesta de fge
  • Interfaces estándar para arrojar excepciones específicas
    • Que aborda la preocupación de Zoltán

Para incluir a Durian en su proyecto, puede:

  • agarrarlo de jcenter o maven central en com.diffplug.durian:durian:3.3.0
  • o simplemente copie y pegue solo dos clases pequeñas en su código: Throwing.java y Errors.java

Esto no es específico de Java 8. Intentas comstackr algo equivalente a:

 interface I { void m(); } class C implements I { public void m() throws Exception {} //can't compile } 

Descargo de responsabilidad: aún no he usado Java 8, solo lo he leído.

Function no arroja IOException , por lo que no puede poner ningún código que throws IOException . Si está llamando a un método que espera una Function , entonces la lambda que pasa a ese método no puede lanzar IOException , punto. Puedes escribir una lambda como esta (creo que esta es la syntax lambda, no estoy seguro):

 (String s) -> { try { return myMethod(s); } catch (IOException ex) { throw new RuntimeException(ex); // (Or do something else with it...) } } 

O bien, si el método al que le está pasando la lambda es uno que usted mismo escribió, puede definir una nueva interfaz funcional y usarla como el tipo de parámetro en lugar de la Function :

 public interface FunctionThatThrowsIOException { O apply(I input) throws IOException; } 

Si no te importa usar una lib de terceros ( Vavr ), podrías escribir

 CheckedFunction1 f = this::myMethod; 

También tiene la llamada Try mónada que maneja errores:

 Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable) .map(i -> ...) // only executed on Success ... 

Por favor, lea más aquí .

Descargo de responsabilidad: soy el creador de Vavr.

Puedes usar el envoltorio para untar

 Function func1 = s -> Unthrow.wrap(() -> myMethod(s)); 

o

 Function func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1); 

Usted puede.

Extender la excepción UtilException de UtilException y agregar genérico donde sea necesario: de esta manera, el comstackdor te obligará nuevamente a agregar cláusulas throw y todo es como si pudieras lanzar excepciones marcadas de forma nativa en las transmisiones de java 8.

 public final class LambdaExceptionUtil { @FunctionalInterface public interface Function_WithExceptions { R apply(T t) throws E; } /** * .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) { throwActualException(exception); return null; } }; } @SuppressWarnings("unchecked") private static  void throwActualException(Exception exception) throws E { throw (E) exception; } } public class LambdaExceptionUtilTest { @Test public void testFunction() throws MyTestException { List sizes = Stream.of("ciao", "hello").map(rethrowFunction(s -> transform(s))).collect(toList()); assertEquals(2, sizes.size()); assertEquals(4, sizes.get(0).intValue()); assertEquals(5, sizes.get(1).intValue()); } private Integer transform(String value) throws MyTestException { if(value==null) { throw new MyTestException(); } return value.length(); } private static class MyTestException extends Exception { } } 

Otra solución que utiliza un contenedor de funciones sería devolver una instancia de un contenedor de su resultado, por ejemplo, Correcto, si todo funcionó bien, ya sea una instancia de, por ejemplo, Error.

Algunos códigos para aclarar cosas:

 public interface ThrowableFunction { B apply(A a) throws Exception; } public abstract class Try { public static boolean isSuccess(Try tryy) { return tryy instanceof Success; } public static  Function> tryOf(ThrowableFunction function) { return a -> { try { B result = function.apply(a); return new Success(result); } catch (Exception e) { return new Failure<>(e); } }; } public abstract boolean isSuccess(); public boolean isError() { return !isSuccess(); } public abstract A getResult(); public abstract Exception getError(); } public class Success extends Try { private final A result; public Success(A result) { this.result = result; } @Override public boolean isSuccess() { return true; } @Override public A getResult() { return result; } @Override public Exception getError() { return new UnsupportedOperationException(); } @Override public boolean equals(Object that) { if(!(that instanceof Success)) { return false; } return Objects.equal(result, ((Success) that).getResult()); } } public class Failure extends Try { private final Exception exception; public Failure(Exception exception) { this.exception = exception; } @Override public boolean isSuccess() { return false; } @Override public A getResult() { throw new UnsupportedOperationException(); } @Override public Exception getError() { return exception; } } 

Un caso de uso simple:

 List> result = Lists.newArrayList(1, 2, 3).stream(). map(Try.tryOf(i -> someMethodThrowingAnException(i))). collect(Collectors.toList()); 

Este problema me ha estado molestando también; esta es la razón por la que he creado este proyecto .

Con eso puedes hacer:

 final ThrowingFunction f = yourMethodReferenceHere; 

Hay una totla de 39 interfaces definidas por el JDK que tienen dicho equivalente de Throwing ; esos son todos @FunctionalInterface s utilizados en las transmisiones (la transmisión base pero también IntStream , LongStream y DoubleStream ).

Y a medida que cada uno de ellos extienda su contraparte que no tira, también puede usarlos directamente en lambdas:

 myStringStream.map(f) // < -- works 

El comportamiento predeterminado es que cuando ThrownByLambdaException lambda arroja una excepción marcada, se lanza una excepción ThrownByLambdaException con la excepción marcada como causa. Por lo tanto, puede capturar eso y obtener la causa.

Otras características están disponibles también.

Tuve este problema con Class.forName y Class.newInstance dentro de una lambda, así que lo hice:

 public Object uncheckedNewInstanceForName (String name) { try { return Class.forName(name).newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } 

Dentro de la lambda, en lugar de llamar a Class.forName (“myClass”). NewInstance () Acabo de llamar a uncheckedNewInstanceForName (“myClass”)

Sin embargo, puedes crear tu propia FunctionalInterface que arroje de la siguiente manera …

 @FunctionalInterface public interface UseInstance { void accept(T instance) throws X; } 

luego impleméntelo usando Lambdas o referencias como se muestra a continuación.

 import java.io.FileWriter; import java.io.IOException; //lambda expressions and the execute around method (EAM) pattern to //manage resources public class FileWriterEAM { private final FileWriter writer; private FileWriterEAM(final String fileName) throws IOException { writer = new FileWriter(fileName); } private void close() throws IOException { System.out.println("close called automatically..."); writer.close(); } public void writeStuff(final String message) throws IOException { writer.write(message); } //... public static void use(final String fileName, final UseInstance block) throws IOException { final FileWriterEAM writerEAM = new FileWriterEAM(fileName); try { block.accept(writerEAM); } finally { writerEAM.close(); } } public static void main(final String[] args) throws IOException { FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet")); FileWriterEAM.use("eam2.txt", writerEAM -> { writerEAM.writeStuff("how"); writerEAM.writeStuff("sweet"); }); FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt); } void writeIt() throws IOException{ this.writeStuff("How "); this.writeStuff("sweet "); this.writeStuff("it is"); } } 

Puedes usar ET para esto. ET es una pequeña biblioteca Java 8 para la conversión / traducción de excepciones.

Con ET se ve así:

 // Do this once ExceptionTranslator et = ET.newConfiguration().done(); ... // if your method returns something Function f = (t) -> et.withReturningTranslation(() -> myMethod(t)); // if your method returns nothing Consumer c = (t) -> et.withTranslation(() -> myMethod(t)); 

ExceptionTranslator instancias de ExceptionTranslator son seguras para hilos y varios componentes las pueden compartir. Puede configurar reglas de conversión de excepciones más específicas (por ejemplo, FooCheckedException -> BarRuntimeException ) si lo desea. Si no hay otras reglas disponibles, las excepciones comprobadas se convierten automáticamente a RuntimeException .

(Descargo de responsabilidad: soy el autor de ET)

Hay muchas respuestas excelentes ya publicadas aquí. Solo bash resolver el problema con una perspectiva diferente. Son solo mis 2 centavos, por favor corrígeme si estoy equivocado en alguna parte.

La cláusula Throws en FunctionalInterface no es una buena idea

Creo que probablemente esta no sea una buena idea para forzar throws IOException por los siguientes motivos

  • Esto me parece un antipatrón para Stream / Lambda. La idea general es que la persona que llama decidirá qué código proporcionar y cómo manejar la excepción. En muchos escenarios, la IOException podría no ser aplicable para el cliente. Por ejemplo, si el cliente obtiene valor de la memoria caché / memoria en lugar de realizar E / S real.

  • Además, el manejo de excepciones en las transmisiones se vuelve realmente horrible. Por ejemplo, aquí está mi código se verá como si utilizo su API

      acceptMyMethod(s -> { try { Integer i = doSomeOperation(s); return i; } catch (IOException e) { // try catch block because of throws clause // in functional method, even though doSomeOperation // might not be throwing any exception at all. e.printStackTrace(); } return null; }); 

    Ugly no es así? Además, como mencioné en mi primer punto, el método doSomeOperation puede o no arrojar IOException (dependiendo de la implementación del cliente / llamante), pero debido a la cláusula throws en su método FunctionalInterface, siempre tengo que escribir el trata de atraparlo.

¿Qué hago si realmente sé que esta API arroja IOException?

  • Entonces probablemente estamos confundiendo FunctionalInterface con interfaces típicas. Si sabes que esta API arrojará IOException, entonces probablemente también conozcas algún comportamiento predeterminado / abstracto. Creo que debes definir una interfaz y desplegar tu biblioteca (con implementación predeterminada / abstracta) de la siguiente manera

     public interface MyAmazingAPI { Integer myMethod(String s) throws IOException; } 

    Pero, el problema de capturar aún existe para el cliente. Si utilizo su API en la transmisión, aún necesito manejar IOException en un horrible bloque try-catch.

  • Proporcione una API compatible con la transmisión predeterminada de la siguiente manera

     public interface MyAmazingAPI { Integer myMethod(String s) throws IOException; default Optional myMethod(String s, Consumer< ? super Exception> exceptionConsumer) { try { return Optional.ofNullable(this.myMethod(s)); } catch (Exception e) { if (exceptionConsumer != null) { exceptionConsumer.accept(e); } else { e.printStackTrace(); } } return Optional.empty(); } } 

    El método predeterminado toma el objeto del consumidor como argumento, que será responsable de manejar la excepción. Ahora, desde el punto de vista del cliente, el código se verá así

     strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace)) .filter(Optional::isPresent) .map(Optional::get).collect(toList()); 

    Agradable ¿verdad? Por supuesto, se puede usar logger u otra lógica de manejo en lugar de Exception :: printStackTrace.

  • También puede exponer un método similar a https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- . Lo que significa que puede exponer otro método, que contendrá la excepción de la llamada al método anterior. La desventaja es que ahora estás haciendo que tus API tengan estado, lo que significa que debes manejar la seguridad de las hebras y que eventualmente se convertirá en un golpe de rendimiento. Solo una opción a considerar sin embargo.

Cree un tipo de devolución personalizado que propagará la excepción marcada. Esta es una alternativa para crear una nueva interfaz que refleje la interfaz funcional existente con la ligera modificación de una “excepción de lanzamiento” en el método de la interfaz funcional.

Definición

CheckedValueSupplier

 public static interface CheckedValueSupplier { public V get () throws Exception; } 

CheckedValue

 public class CheckedValue { private final V v; private final Optional opt; public Value (V v) { this.v = v; } public Value (Exception e) { this.opt = Optional.of(e); } public V get () throws Exception { if (opt.isPresent()) { throw opt.get(); } return v; } public Optional getException () { return opt; } public static  CheckedValue returns (T t) { return new CheckedValue(t); } public static  CheckedValue rethrows (Exception e) { return new CheckedValue(e); } public static  CheckedValue from (CheckedValueSupplier sup) { try { return CheckedValue.returns(sup.get()); } catch (Exception e) { return Result.rethrows(e); } } public static  CheckedValue escalates (CheckedValueSupplier sup) { try { return CheckedValue.returns(sup.get()); } catch (Exception e) { throw new RuntimeException(e); } } } 

Uso

 // Don't use this pattern with FileReader, it's meant to be an // example. FileReader is a Closeable resource and as such should // be managed in a try-with-resources block or in another safe // manner that will make sure it is closed properly. // This will not compile as the FileReader constructor throws // an IOException. Function sToFr = (fn) -> new FileReader(Paths.get(fn).toFile()); // Alternative, this will compile. Function> sToFr = (fn) -> { return CheckedValue.from ( () -> new FileReader(Paths.get("/home/" + f).toFile())); }; // Single record usage // The call to get() will propagate the checked exception if it exists. FileReader readMe = pToFr.apply("/home/README").get(); // List of records usage List paths = ...; //a list of paths to files Collection> frs = paths.stream().map(pToFr).collect(Collectors.toList()); // Find out if creation of a file reader failed. boolean anyErrors = frs.stream() .filter(f -> f.getException().isPresent()) .findAny().isPresent(); 

¿Que esta pasando?

Agregar “throws exception” a cada interfaz funcional en el JDK violaría el principio DRY de la manera más atroz. Para evitar esto, se crea una única interfaz funcional que arroja una excepción marcada ( CheckedValueSupplier ). Esta será la única interfaz funcional que permite excepciones marcadas. Todas las demás interfaces funcionales aprovecharán el CheckedValueSupplier para ajustar cualquier código que arroje una excepción marcada.

La clase CheckedValue contendrá el resultado de ejecutar cualquier lógica que arroje una excepción marcada. Esto evita la propagación de una excepción comprobada hasta el punto en el que el código intente acceder al valor que contiene una instancia de CheckedValue .

Los problemas con este enfoque.

  • Ahora estamos lanzando “Exception” efectivamente ocultando el tipo específico lanzado originalmente.
  • No nos damos cuenta de que se produjo una excepción hasta que se CheckedValue#get() .

Consumer et al

Algunas interfaces funcionales (por ejemplo, el Consumer ) deben manejarse de una manera diferente ya que no proporcionan un valor de retorno.

Función en lugar del consumidor

Un enfoque es usar una función en lugar de un consumidor, que se aplica al manejar flujos.

  List lst = Lists.newArrayList(); // won't compile lst.stream().forEach(e -> throwyMethod(e)); // compiles lst.stream() .map(e -> CheckedValueSupplier.from( () -> {throwyMethod(e); return e;})) .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior 

Escalar

Alternativamente, siempre puede escalar a una RuntimeException . Hay otras respuestas que cubren la escalada de una excepción comprobada dentro de un Consumer .

No consums

Solo evite todas las interfaces funcionales y use un bucle for bien desarrollado.

Sneaky throw idiom permite eludir CheckedException de la expresión Lambda. Envolver una CheckedException en una RuntimeException no es buena para el manejo estricto de errores.

Se puede usar como una función de Consumer utilizada en una colección de Java.

Aquí hay una versión simple y mejorada de la respuesta de jib .

 import static Throwing.rethrow; @Test public void testRethrow() { thrown.expect(IOException.class); thrown.expectMessage("i=3"); Arrays.asList(1, 2, 3).forEach(rethrow(e -> { int i = e.intValue(); if (i == 3) { throw new IOException("i=" + i); } })); } 

Esto acaba de envolver a la lambda en un nuevo lanzamiento . Hace que CheckedException lanzar cualquier Exception que haya sido lanzada en su lambda.

 public final class Throwing { private Throwing() {} @Nonnull public static  Consumer rethrow(@Nonnull final ThrowingConsumer consumer) { return consumer; } /** * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it * allows the unchecked exception to propagate. * * http://www.baeldung.com/java-sneaky-throws */ @SuppressWarnings("unchecked") @Nonnull public static  void sneakyThrow(@Nonnull Throwable ex) throws E { throw (E) ex; } } 

Encuentre aquí un código completo y pruebas unitarias.

Si no te importa usar una biblioteca de terceros, con cyclops-react , una biblioteca a la que contribuyo, puedes usar la API FluentFunctions para escribir

  Function standardFn = FluentFunctions.ofChecked(this::myMethod); 

ofChecked toma una función CheckedFun de jOOλ y devuelve la referencia suavizada a una función JDK java.util.function.Function estándar (sin marcar).

Alternativamente, puede seguir trabajando con la función capturada a través de la API FluentFunctions.

Por ejemplo, para ejecutar su método, reintentarlo hasta 5 veces y registrar su estado, puede escribir

  FluentFunctions.ofChecked(this::myMethod) .log(s->log.debug(s),e->log.error(e,e.getMessage()) .try(5,1000) .apply("my param"); 

Por defecto, la función Java 8 no permite lanzar una excepción y, como se sugiere en varias respuestas, hay muchas maneras de lograrlo, una forma es:

 @FunctionalInterface public interface FunctionWithException { R apply(T t) throws E; } 

Definir como:

 private FunctionWithException myMethod = (str) -> { if ("abc".equals(str)) { throw new IOException(); } return 1; }; 

Y agrega throws o try/catch the same exception en el método de llamada.

Lo que estoy haciendo es permitir que el usuario dé el valor que realmente quiere en caso de excepción. Así que tengo algo así como esto

 public static  Function< ? super T, ? extends R> defaultIfThrows(FunctionThatThrows< ? super T, ? extends R> delegate, R defaultValue) { return x -> { try { return delegate.apply(x); } catch (Throwable throwable) { return defaultValue; } }; } @FunctionalInterface public interface FunctionThatThrows { R apply(T t) throws Throwable; } 

Y esto puede llamarse así:

 defaultIfThrows(child -> child.getID(), null) 

Varias de las soluciones ofrecidas usan un argumento genérico de E para pasar en el tipo de excepción que se arroja.

Avance un paso más, y en lugar de pasar el tipo de excepción, pase un consumidor del tipo de excepción, como en …

 Consumer 

Puede crear varias variaciones reutilizables de Consumer que cubran las necesidades comunes de manejo de excepciones de su aplicación.

Haré algo genérico:

 public interface Lambda { @FunctionalInterface public interface CheckedFunction { T get() throws Exception; } public static  T handle(CheckedFunction supplier) { try { return supplier.get(); } catch (Exception exception) { throw new RuntimeException(exception); } } } 

uso:

  Lambda.handle(() -> method()); 

Usa Jool Library o di la jOOλ library JOOQ de JOOQ . No solo proporciona interfaces manejadas por excepciones no comprobadas, sino que también proporciona una clase Seq con muchos métodos útiles.

Además, contiene interfaces funcionales con hasta 16 parámetros. Además, proporciona una clase Tuple que se usa en diferentes escenarios.

Jool Git Link

Específicamente en la búsqueda de bibliotecas para el paquete org.jooq.lambda.fi.util.function . Contiene todas las interfaces de Java-8 con Checked prepended. Ver a continuación para referencia: –

enter image description here

 public void frankTest() { int pageId= -1; List users= null; try { //Does Not Compile: Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name"))); //Compiles: Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { try { final Book bk= users.stream().filter(bp -> { String name= null; try { name = rw.getString("name"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return bp.getTitle().equals(name); }).limit(1).collect(Collectors.toList()).get(0); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); } ); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }