tipos Java 8: Como trabalho com métodos de lançamento de exceção em fluxos?



throw new exception java (6)

Maneira mais legível:

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

Apenas esconda em um RuntimeException para passar por forEach()

  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();
      }
  }

Embora neste momento eu não segure contra alguém se eles disserem que pulam os streams e vão com um loop for, a menos que:

  • você não está criando seu fluxo usando Collection.stream() , ou seja, não é uma tradução direta para um loop for.
  • você está tentando usar parallelstream()

Suponha que eu tenha uma classe e um método

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

Agora eu gostaria de chamar foo para cada instância de A entregue por um fluxo como:

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

Pergunta: Como faço para lidar corretamente com a exceção? O código não compila na minha máquina porque não manipulo as possíveis exceções que podem ser lançadas pelo foo (). A throws Exception de bar parece ser inútil aqui. Por que é que?


Answer #1

Eu sugiro usar a classe Google Guava Throwables

Propagar ( throwável )

Propaga throwable como está se for uma instância de RuntimeException ou Error, ou então como último recurso, encapsula-o em uma RuntimeException e depois se propaga. **

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

ATUALIZAR:

Agora que está obsoleto, use:

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

Answer #2

Você pode quebrar e desembrulhar exceções dessa maneira.

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);
    }
}

Answer #3

Você precisa envolver sua chamada de método em outra, onde você não lança exceções verificadas . Você ainda pode lançar qualquer coisa que seja uma subclasse de RuntimeException .

Uma expressão normal é algo como:

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

( Exception supertipo A exceção é usada apenas como exemplo, nunca tente pegá-lo)

Então você pode chamá-lo com: as.forEach(this::safeFoo) .


Answer #4

Você pode querer fazer um dos seguintes:

  • propagar exceção verificada,
  • envolvê-lo e propagar exceção não verificada, ou
  • pegue a exceção e pare a propagação.

Várias bibliotecas permitem que você faça isso facilmente. Exemplo abaixo é escrito usando minha 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)));

Answer #5

Esta questão pode ser um pouco antiga, mas porque eu acho que a resposta "certa" aqui é apenas uma maneira que pode levar a alguns problemas ocultos mais tarde no seu código. Mesmo se houver um pouco de Controversy , existem exceções verificadas por um motivo.

A maneira mais elegante, na minha opinião, que você pode encontrar foi dada por Misha aqui Agregar exceções de tempo de execução em fluxos de Java 8 , apenas realizando as ações em "futuros". Assim, você pode executar todas as partes de trabalho e coletar exceções que não funcionam como uma única. Caso contrário, você pode coletá-los todos em uma lista e processá-los mais tarde.

Uma abordagem semelhante vem de Benji Weber . Ele sugere criar um tipo próprio para coletar peças em funcionamento e não em funcionamento.

Dependendo do que você realmente deseja alcançar um mapeamento simples entre os valores de entrada e os valores de saída, as exceções também podem funcionar para você.

Se você não gosta de nenhuma destas maneiras, considere usar (dependendo da Exceção Original) pelo menos uma exceção própria.





java-stream