¿Función Lambda de Java 8 que arroja una excepción?
Sé cómo crear una referencia a un método que tiene un String
parámetro y devuelve un int
, es:
Function<String, Integer>
Sin embargo, esto no funciona si la función genera una excepción, digamos que está definida como:
Integer myMethod(String s) throws IOException
¿Cómo definiría esta referencia?
Deberá realizar una de las siguientes acciones.
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 usarlo:
void foo (CheckedFunction f) { ... }
De lo contrario, incluya
Integer myMethod(String s)
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 luego:
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); } };
De hecho, puede ampliar Consumer
(y Function
etc.) con una nueva interfaz que maneje excepciones, ¡utilizando los métodos predeterminados de Java 8 !
Considere esta interfaz (se extiende 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 genere 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, puedes 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 dígalo para que sea más conciso!:
list.forEach((ThrowingConsumer<String>) aps -> {
// maybe some other code here...
throw new Exception("asda");
});
Actualizar
Parece que hay una muy buena biblioteca de utilidades de Durian llamada Errores que puede usarse para resolver este problema con mucha más flexibilidad. Por ejemplo, en mi implementación anterior definí 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 gran 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 de DurianErrors
combina muchas de las ventajas de las diversas sugerencias anteriores.
- Envuelva una función de lanzamiento en una interfaz funcional estándar de Java 8.
- Especifique fácilmente varias políticas para manejar errores
- Al empaquetar un método que devuelve un valor, existe una distinción importante entre especificar un valor predeterminado o volver a generar una RuntimeException.
- Lanzamiento de versiones de las interfaces funcionales de Java 8
- Similar a la respuesta de fge
- Interfaces estándar para lanzar excepciones específicas
- Que atiende la preocupación de Zoltán
Para incluir Durian en su proyecto, puede:
- tómalo 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
yErrors.java
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
}
Descargo de responsabilidad: todavía no he usado Java 8, solo leí sobre él.
Function<String, Integer>
no arroja IOException
, por lo que no puedes poner ningún código en él throws IOException
. Si llama a un método que espera un Function<String, Integer>
, entonces la lambda que pasa a ese método no puede arrojar IOException
un punto. Puedes escribir una lambda como esta (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 usted mismo escribió, puede definir una nueva interfaz funcional y usarla como tipo de parámetro en lugar de Function<String, Integer>
:
public interface FunctionThatThrowsIOException<I, O> {
O apply(I input) throws IOException;
}