Java 8: ¿Cómo trabajo con métodos de lanzamiento de excepciones en secuencias?

172

Supongamos que tengo una clase y un método

class A {
  void foo() throws Exception() {
    ...
  }
}

Ahora me gustaría llamar a foo para cada instancia de Aentregado por una secuencia como:

void bar() throws Exception {
  Stream<A> as = ...
  as.forEach(a -> a.foo());
}

Pregunta: ¿Cómo manejo adecuadamente la excepción? El código no se compila en mi máquina porque no manejo las posibles excepciones que puede generar foo (). El throws Exceptionde barparece ser inútil aquí. ¿Porqué es eso?

Bastian
fuente
Relacionado: stackoverflow.com/q/30117134/435605
AlikElzin-kilaka

Respuestas:

141

Debe ajustar su llamada al método en otra, donde no arroje excepciones marcadas . Todavía puedes lanzar cualquier cosa que sea una subclase de RuntimeException.

Un lenguaje de envoltura normal es algo como:

private void safeFoo(final A a) {
    try {
        a.foo();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

(Supertype excepción Exceptiones solamente usado como ejemplo, nunca tratar de capturarlo por sí solo)

A continuación, se le puede llamar con: as.forEach(this::safeFoo).

skiwi
fuente
1
Si desea que sea un método de envoltura, lo declararía estático. No usa nada de 'esto'.
aalku
207
Es triste que tengamos que hacer esto en lugar de usar nuestras excepciones nativas ... oh Java, da y luego quita
Erich
1
@Stephan Esa respuesta se eliminó, pero aún está disponible aquí: stackoverflow.com/a/27661562/309308
Michael Mrozek
3
Eso fallaría instantáneamente en la revisión del código en mi empresa: no se nos permite lanzar excepciones no verificadas.
Stelios Adamantidis
77
@SteliosAdamantidis esa regla es increíblemente restrictiva y contraproducente. ¿eres consciente de que todos los métodos, en todas las API, están autorizados a lanzar todos los sabores de las excepciones de tiempo de ejecución sin siquiera tener que saberlo (por supuesto que sí)? ¿prohibió javascript por no tener implementado el concepto de excepciones verificadas? Si yo fuera su desarrollador principal, prohibiría las excepciones marcadas.
spi
35

Si todo lo que desea es invocar foo, y prefiere propagar la excepción tal como está (sin envolver), también puede usar el forbucle de Java en su lugar (después de convertir el Stream en un Iterable con algunos trucos ):

for (A a : (Iterable<A>) as::iterator) {
   a.foo();
}

Esto es, al menos, lo que hago en mis pruebas JUnit, donde no quiero pasar por el problema de envolver mis excepciones marcadas (y de hecho prefiero que mis pruebas arrojen las originales sin envolver)

avandeursen
fuente
16

Esta pregunta puede ser un poco antigua, pero porque creo que la respuesta "correcta" aquí es solo una forma que puede conducir a algunos problemas Problemas ocultos más adelante en su código. Incluso si hay un poco de controversia , existen excepciones marcadas por alguna razón.

La forma más elegante, en mi opinión, puede encontrarla fue dada por Misha aquí . Agregue excepciones de tiempo de ejecución en las secuencias de Java 8 simplemente realizando las acciones en "futuros". Por lo tanto, puede ejecutar todas las partes de trabajo y recopilar Excepciones que no funcionan como una sola. De lo contrario, podría recopilarlos todos en una Lista y procesarlos más tarde.

Un enfoque similar proviene de Benji Weber . Sugiere crear un tipo propio para recopilar piezas de trabajo y no piezas de trabajo.

Dependiendo de lo que realmente desea lograr, se produjo una asignación simple entre los valores de entrada y los valores de salida. Las excepciones también pueden funcionar para usted.

Si no le gusta ninguna de estas formas, considere usar (según la excepción original) al menos una excepción propia.

Mariano
fuente
3
Esto debe ser marcado como una respuesta correcta. +. Pero no puedo decir que sea una buena publicación. Deberías elaborarlo mucho. ¿Cómo podría ayudar la propia excepción? ¿Cuáles son las excepciones ocurridas? ¿Por qué no has traído las diferentes variantes aquí? Tener todo el código de ejemplo en las referencias se considera un estilo de publicación incluso prohibido aquí en SO. Su texto parece más bien un montón de comentarios.
Gangnus
2
Debería poder elegir en qué nivel desea atraparlos y las transmisiones se enredan con eso. La API Stream debería permitirle llevar la excepción hasta la operación final (como recoger) y manejarla allí con un controlador o arrojarla de otra manera. stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...). Debe esperar excepciones y le permite manejarlas como lo hacen los futuros.
aalku
10

Sugiero usar la clase Google Guava Throwables

propagar ( arrojable )

Se propaga tal como está si es una instancia de RuntimeException o Error, o como último recurso, lo envuelve en una RuntimeException y luego se propaga. **

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            throw Throwables.propagate(e);
        }
    });
}

ACTUALIZAR:

Ahora que está en desuso, use:

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        }
    });
}
yanefedor
fuente
44
Este método fue desaprobado (desafortunadamente).
Robert Važan
9

Puede envolver y desenvolver excepciones de esta manera.

class A {
    void foo() throws Exception {
        throw new Exception();
    }
};

interface Task {
    void run() throws Exception;
}

static class TaskException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public TaskException(Exception e) {
        super(e);
    }
}

void bar() throws Exception {
      Stream<A> as = Stream.generate(()->new A());
      try {
        as.forEach(a -> wrapException(() -> a.foo())); // or a::foo instead of () -> a.foo()
    } catch (TaskException e) {
        throw (Exception)e.getCause();
    }
}

static void wrapException(Task task) {
    try {
        task.run();
    } catch (Exception e) {
        throw new TaskException(e);
    }
}
aalku
fuente
9

Es posible que desee hacer uno de los siguientes:

  • propagar excepción comprobada,
  • envolverlo y propagar una excepción no marcada, o
  • captura la excepción y detiene la propagación.

Varias bibliotecas le permiten hacer eso fácilmente. El siguiente ejemplo está escrito usando mi biblioteca NoException .

// Propagate checked exception
as.forEach(Exceptions.sneak().consumer(A::foo));

// Wrap and propagate unchecked exception
as.forEach(Exceptions.wrap().consumer(A::foo));
as.forEach(Exceptions.wrap(MyUncheckedException::new).consumer(A::foo));

// Catch the exception and stop propagation (using logging handler for example)
as.forEach(Exceptions.log().consumer(Exceptions.sneak().consumer(A::foo)));
Robert Važan
fuente
Buen trabajo en la biblioteca! Estaba a punto de escribir algo similar.
Jasper de Vries
2

Forma más legible:

class A {
  void foo() throws MyException() {
    ...
  }
}

Solo escóndelo en un RuntimeExceptionpara pasarloforEach()

  void bar() throws MyException {
      Stream<A> as = ...
      try {
          as.forEach(a -> {
              try {
                  a.foo();
              } catch(MyException e) {
                  throw new RuntimeException(e);
              }
          });
      } catch(RuntimeException e) {
          throw (MyException) e.getCause();
      }
  }

Aunque en este punto no me aguantaré contra alguien si dice omitir las transmisiones e ir con un bucle for, a menos que:

  • no está creando su transmisión usando Collection.stream(), es decir, no es una traducción directa a un bucle for.
  • estás tratando de usar parallelstream()
Kashyap
fuente