Excepción no controlada de referencia de método Java 8

Estoy trabajando en un proyecto con Java 8 y encontré una situación que no puedo entender.

Tengo un código como este:

void deleteEntity(Node node) throws SomeException { for (ChildNode child: node.getChildren()) { deleteChild(child); } } void deleteChild(Object child) throws SomeException { //some code } 

Este código funciona bien, pero puedo reescribirlo con una referencia de método:

 void deleteEntity(Node node) throws SomeException { node.getChildren().forEach(this::deleteChild); } 

Y este código no se comstack, dando el error Incompatible thrown types *SomeException* in method reference .

También IDEA me dio el error de unhandled exception .

¿Mi pregunta es, porque? ¿Por qué se comstack el código para cada ciclo y no se comstack con lambda?

Si observa la interfaz de Consumer , no se declara que el método de accept (que es el que usaría su referencia de método) arroja excepciones marcadas; por lo tanto, no puede usar una referencia de método que se declare para arrojar una excepción marcada El bucle for mejorado está bien, porque allí siempre estás en un contexto donde se puede lanzar SomeException .

Podría crear potencialmente un contenedor que convierta la excepción marcada en una excepción sin marcar y lanzarla. Alternativamente, puede declarar su propia interfaz funcional con un método accept() que emite una excepción marcada (probablemente parametrizando la interfaz con esa excepción), y luego escribe la suya para forEach método que toma esa interfaz funcional como una entrada.

Puedes intentar esto:

 void deleteEntity(Node node) throws SomeException { node.getChildren().forEach(UtilException.rethrowConsumer(this::deleteChild)); } 

La clase auxiliar UtilException continuación le permite usar cualquier excepción marcada en las secuencias de Java. Tenga en cuenta que la secuencia anterior también arroja la excepción marcada original lanzada por this::deleteChild , y NO una excepción de this::deleteChild .

 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 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 agregarla a la cláusula throws del método que contiene la transmisión. El comstackdor no lo obligará a agregarlo más, por lo que es más fácil olvidarlo.

• Si el código de llamada ya maneja la excepción marcada, el comstackdor LE RECORDARÁ que agregue la cláusula throws a la statement del método que contiene la secuencia (si no lo hace dirá: Exception nunca se arroja en el cuerpo de la instrucción Try correspondiente )

• En cualquier caso, no podrá rodear la secuencia en sí para capturar la excepción marcada DENTRO del método que contiene la secuencia (si lo intenta, el comstackdor dirá: La excepción nunca se arroja en el cuerpo de la statement 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 UTF-8 está garantizado por las especificaciones de Java para estar siempre presente. Aquí, la statement de los lanzamientos es una molestia y cualquier solución para silenciarla con un mínimo es bienvenido.

• Si odias verificaciones de excepciones y crees que nunca deberían agregarse al lenguaje Java, para empezar (un número creciente de personas piensa de esta manera, y yo NO soy uno de ellos), simplemente no agregues la excepción marcada al arroja la cláusula del método que contiene la secuencia. La excepción marcada se comportará, entonces, como una excepción no seleccionada.

• Si está implementando una interfaz estricta donde no tiene la opción de agregar una statement de lanzamientos, y sin embargo, lanzar una excepción es completamente apropiado, entonces al envolver una excepción solo para obtener el privilegio de lanzarla se genera una stack con excepciones espúreas 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á atraparlo por su nombre (si lo intenta, el comstackdor dirá: La excepción nunca se arroja en el cuerpo de la statement try correspondiente). Burbujeará y probablemente quede atrapado en el bucle principal del progtwig por alguna “excepción de captura” o “captura Throwable”, que puede ser lo que quieras de todos modos.

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

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

También puede declarar someException para que extienda RuntimeException lugar de Exception . El siguiente código de ejemplo se comstackrá:

 public class Test { public static void main(String[] args){ // TODO Auto-generated method stub List test = new ArrayList(); test.add("foo"); test.add(null); test.add("bar"); test.forEach(x -> print(x)); } public static class SomeException extends RuntimeException{ } public static void print(String s) throws SomeException{ if (s==null) throw new SomeException(); System.out.println(s); } } 

La salida será entonces:

 foo Exception in thread "main" simpleTextLayout.Test$SomeException at simpleTextLayout.Test.print(Test.java:22) at simpleTextLayout.Test.lambda$0(Test.java:14) at java.util.ArrayList.forEach(ArrayList.java:1249) at simpleTextLayout.Test.main(Test.java:14) 

Puede agregar un bloque try/catch alrededor de la instrucción forEach , sin embargo, la ejecución de la instrucción forEach se interrumpirá una vez que se haya lanzado una excepción. En el ejemplo anterior, el elemento "bar" de la lista no se imprimirá. Además, al hacer eso, perderás la pista de la excepción lanzada en tu IDE.

** Si no desea escribir su propia interfaz del consumidor y hacer uso de ella. Puede usar su excepción personalizada con facilidad, como se muestra a continuación. Puede realizar lo siguiente. **

 list.stream().forEach(x->{ try{ System.out.println(x/0); }catch(ArithmeticException e){ throw new RuntimeException(new MyCustomException(FirstArgument,SecondArgument)); });