tutorial lambdas expressions example java lambda java-8

java - lambdas - lambda expressions



¿Función de Java 8 Lambda que lanza excepción? (23)

Cree un tipo de retorno personalizado que propagará la excepción comprobada. Esta es una alternativa a la creación de una nueva interfaz que refleje la interfaz funcional existente con la ligera modificación de una "excepción de lanzamientos" en el método de la interfaz funcional.

Definición

CheckedValueSupplier

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

Valor verificado

public class CheckedValue<V> { private final V v; private final Optional<Exception> 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<Exception> getException () { return opt; } public static <T> CheckedValue<T> returns (T t) { return new CheckedValue<T>(t); } public static <T> CheckedValue<T> rethrows (Exception e) { return new CheckedValue<T>(e); } public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) { try { return CheckedValue.returns(sup.get()); } catch (Exception e) { return Result.rethrows(e); } } public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> 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<String, FileReader> sToFr = (fn) -> new FileReader(Paths.get(fn).toFile()); // Alternative, this will compile. Function<String, CheckedValue<FileReader>> 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<String> paths = ...; //a list of paths to files Collection<CheckedValue<FileReader>> 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 "excepciones de lanzamientos" 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 lanza una excepción marcada ( CheckedValueSupplier ). Esta será la única interfaz funcional que permite excepciones comprobadas. Todas las demás interfaces funcionales aprovecharán CheckedValueSupplier para envolver cualquier código que arroje una excepción marcada.

La clase CheckedValue mantendrá 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 intenta acceder al valor que contiene una instancia de CheckedValue .

Los problemas con este enfoque.

  • Ahora estamos lanzando "Excepción" ocultando efectivamente el tipo específico lanzado originalmente.
  • Desconocemos que ocurrió una excepción hasta que se CheckedValue#get() .

Consumer et al

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

Función en lugar de consumidor

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

List<String> 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 marcada desde un Consumer .

No consumas.

Simplemente evite las interfaces funcionales todas juntas y use un bucle de buena forma.

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

Function<String, Integer>

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

Integer myMethod(String s) throws IOException

¿Cómo definiría esta referencia?


Creo que Errors combina muchas de las ventajas de las diversas sugerencias anteriores.

Para incluir a Durian en su proyecto, puede:


De forma predeterminada, la Function Java 8 no permite lanzar excepciones y, como se sugiere en varias respuestas, hay muchas maneras de lograrlo, una forma es:

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

Definir como:

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

Y agregue throws o try/catch la misma excepción en el método del llamador.


En realidad, puede ampliar el Consumer (y la Function etc.) con una nueva interfaz que maneja las excepciones, ¡utilizando los métodos predeterminados de Java 8!

Considere esta interfaz (se extiende al Consumer ):

@FunctionalInterface public interface ThrowingConsumer<T> extends Consumer<T> { @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 tienes una lista:

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

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

final Consumer<String> 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 compilador no se quejará:

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

O incluso simplemente lanzarlo para ser más sucinto !:

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

Actualización : Parece que hay una muy buena parte de la biblioteca de utilidades de Durian llamada Errors 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 una gran variedad de métodos de utilidad. Gracias por compartirlo , @NedTwigg !.

Uso de la muestra:

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


Este problema me ha estado molestando también; Por eso he creado este proyecto .

Con ello podrás hacer:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

Hay una totla de 39 interfaces definidas por el JDK que tienen ese equivalente de Throwing ; Todos estos son @FunctionalInterface s utilizados en secuencias (la Stream base pero también IntStream , LongStream y DoubleStream ).

Y como cada uno de ellos extiende su contraparte de no lanzar, también puede usarlos directamente en las lambdas:

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

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

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


Esto no es específico de Java 8. Está intentando compilar algo equivalente a:

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


Haré algo genérico:

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

uso:

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


Hay muchas respuestas geniales ya publicadas aquí. Solo intento resolver el problema con una perspectiva diferente. Son solo mis 2 centavos, corrígeme si me equivoco en alguna parte.

La cláusula de lanzamiento en FunctionalInterface no es una buena idea

Creo que probablemente esta no sea una buena idea para imponer la excepción IOException debido a las siguientes razones

  • Esto me parece un anti-patró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 casos, la excepción 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 la E / S real.

  • Además, el manejo de excepciones en las secuencias se vuelve realmente horrible. Por ejemplo, aquí está mi código se verá como si utilizara 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; });

    Feo no es? Además, como mencioné en mi primer punto, el método doSomeOperation puede o no lanzar 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 produce IOException?

  • Entonces, probablemente estamos confundiendo FunctionalInterface con interfaces típicas. Si sabe que esta API lanzará IOException, entonces probablemente también conozca algún comportamiento predeterminado / abstracto. Creo que debería definir una interfaz e implementar su biblioteca (con implementación predeterminada / abstracta) de la siguiente manera

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

    Pero, el problema try-catch todavía existe para el cliente. Si utilizo su API en secuencia, todavía necesito manejar la excepción IOException en el horrible bloque try-catch.

  • Proporcione una API por defecto para la transmisión de la siguiente manera

    public interface MyAmazingAPI { Integer myMethod(String s) throws IOException; default Optional<Integer> 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 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());

    Bien cierto? Por supuesto, se podría usar el 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á logrando que sus API sean con estado, lo que significa que necesita manejar la seguridad de subprocesos y que eventualmente se convertirá en un éxito de rendimiento. Sin embargo, sólo una opción a considerar.


Lo que estoy haciendo es permitir al usuario dar el valor que realmente desea en caso de excepción. Así que tengo algo parecido a esto

public static <T, R> 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<T, R> { R apply(T t) throws Throwable; }

Y esto puede ser llamado como:

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


Otra solución que utilice un envoltorio de función sería devolver una instancia de un envoltorio de su resultado, digamos Éxito, si todo salió bien, o bien una instancia de, digamos Error.

Algún código para aclarar las cosas:

public interface ThrowableFunction<A, B> { B apply(A a) throws Exception; } public abstract class Try<A> { public static boolean isSuccess(Try tryy) { return tryy instanceof Success; } public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) { return a -> { try { B result = function.apply(a); return new Success<B>(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<A> extends Try<A> { 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<A> extends Try<A> { 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<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream(). map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))). collect(Collectors.toList());


Puedes usar ET para esto. ET es una pequeña biblioteca de Java 8 para 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<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t)); // if your method returns nothing Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));

ExceptionTranslator instancias de ExceptionTranslator son seguras para subprocesos y pueden ser compartidas por varios componentes. 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 marcadas se convierten automáticamente a RuntimeException .

(Descargo de responsabilidad: Soy el autor de ET)


Puedes usar la envoltura de unthrow

Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));

o

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


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

Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

ofChecked toma un jOOλ CheckedFunction y devuelve la referencia suavizada a un JDK java.util.function.Function estándar (sin marcar).

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

Por ejemplo, para ejecutar su método, reinténtelo hasta 5 veces y registrando 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");


Si no te importa utilizar una biblioteca de terceros ( Vavr ), puedes escribir

CheckedFunction1<String, Integer> f = this::myMethod;

También tiene la llamada Tríada de prueba que maneja los errores:

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

Por favor lea más here .

Descargo de responsabilidad: Soy el creador de Vavr.


Sin embargo, puede crear su propio FunctionalInterface que se lanza como se muestra a continuación.

@FunctionalInterface public interface UseInstance<T, X extends Throwable> { void accept(T instance) throws X; }

luego implementarlo utilizando 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<FileWriterEAM, IOException> 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"); } }


Tendrá que hacer uno de los siguientes.

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

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

    y usalo

    void foo (CheckedFunction f) { ... }

  • De lo contrario, Integer myMethod(String s) en un método que no declare una excepción comprobada:

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

    y entonces

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);

    o

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


Tuve este problema con Class.forName y Class.newInstance dentro de un lambda, así que simplemente 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 uncheckedNewInstanceForName ("myClass")


Use la Jool Library o diga la jOOλ library JOOQ de JOOQ . No solo proporciona interfaces manejadas por excepciones sin verificar, sino que también proporciona a la clase Seq muchos métodos útiles.

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

jool

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


Usted puede.

Extendiendo UtilException de UtilException y agregando <E extends Exception> cuando sea necesario: de esta manera, el compilador te obligará nuevamente a agregar cláusulas de lanzamiento 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<T, R, E extends Exception> { R apply(T t) throws E; } /** * .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) { throwActualException(exception); return null; } }; } @SuppressWarnings("unchecked") private static <E extends Exception> void throwActualException(Exception exception) throws E { throw (E) exception; } } public class LambdaExceptionUtilTest { @Test public void testFunction() throws MyTestException { List<Integer> sizes = Stream.of("ciao", "hello").<Integer>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 { } }


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

Vaya un paso más allá, y en lugar de pasar el tipo de excepción, pase un Consumidor del tipo de excepción, como en ...

Consumer<E extends Exception>

Puede crear varias variaciones reutilizables de Consumer<Exception> del Consumer<Exception> que cubrirían las necesidades comunes de manejo de excepciones de su aplicación.


El lenguaje de lanzamiento CheckedException permite eludir la CheckedException de la expresión Lambda. CheckedException una CheckedException en una CheckedException RuntimeException no es bueno para el manejo estricto de errores.

Se puede utilizar 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 simplemente envuelve a la lambda en un rebrote . Hace que CheckedException cualquier Exception que haya sido lanzada en su lambda.

public final class Throwing { private Throwing() {} @Nonnull public static <T> Consumer<T> rethrow(@Nonnull final ThrowingConsumer<T> 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 <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E { throw (E) ex; } }

Encuentra un código completo y pruebas de unidad here .


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

Function<String, Integer> no lanza la IOException , por lo que no puede poner ningún código en ella que throws IOException . Si está llamando a un método que espera una Function<String, Integer> , entonces el lambda que pasa a ese método no puede lanzar IOException , punto. Puedes escribir un lambda como este (creo que esta es la sintaxis lambda, no estoy seguro):

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

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

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


public void frankTest() { int pageId= -1; List<Book> 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(); } }