¿Función Lambda de Java 8 que arroja una excepción?

Resuelto Rocky Pulley asked hace 11 años • 28 respuestas

Sé cómo crear una referencia a un método que tiene un Stringpará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?

Rocky Pulley avatar Aug 13 '13 06:08 Rocky Pulley
Aceptado

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);
            }
        };
    
jason avatar Aug 12 '2013 23:08 jason

De hecho, puede ampliar Consumer(y Functionetc.) 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)));
jlb avatar Dec 02 '2014 14:12 jlb

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 encom.diffplug.durian:durian:3.3.0
  • o simplemente copie y pegue solo dos clases pequeñas en su código: Throwing.javayErrors.java
Ned Twigg avatar May 14 '2015 19:05 Ned Twigg

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
}
assylias avatar Aug 12 '2013 23:08 assylias

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 IOExceptionun 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;
}
Adam R. Nelson avatar Aug 12 '2013 23:08 Adam R. Nelson