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

172

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 Aentregue por um fluxo como:

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

Pergunta: Como manejo adequadamente a exceção? O código não é compilado na minha máquina porque eu não manejo as possíveis exceções que podem ser lançadas por foo (). O throws Exceptionde barparece ser inútil aqui. Por que é que?

Bastian
fonte
Relacionados: stackoverflow.com/q/30117134/435605
AlikElzin-Kilaka

Respostas:

141

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

Um idioma de agrupamento normal é algo como:

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

(A exceção Supertype Exceptioné usada apenas como exemplo, nunca tente capturá-la)

Depois, você pode chamá-lo com: as.forEach(this::safeFoo).

skiwi
fonte
1
Se você quiser que seja um método de invólucro, eu o declararia estático. Ele não usa nada de 'this'.
Aalku
207
É triste que temos que fazer isso em vez de usar os nossos exceções nativas .... oh Java, que dá e depois tira
Erich
1
@Stephan Essa resposta foi excluído, mas ele ainda está disponível aqui: stackoverflow.com/a/27661562/309308
Michael Mrozek
3
Isso falharia instantaneamente na revisão de código na minha empresa: não temos permissão para lançar exceções não verificadas.
Stelios Adamantidis 28/05
7
@SteliosAdamantidé essa regra é incrivelmente restritiva e contraproducente. você está ciente de que todos os métodos, em todas as APIs, estão autorizados a lançar todos os tipos de exceções de tempo de execução sem que você precise conhecê-lo (é claro que você é)? você proibiu o javascript por não ter implementado o conceito de exceções verificadas? Se eu fosse seu desenvolvedor líder, baniria as exceções verificadas.
Spi
35

Se tudo o que você deseja é chamar fooe preferir propagar a exceção como está (sem quebra), você também pode usar o forloop do Java (depois de transformar o Stream em um Iterable com alguns truques ):

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

Isso é, pelo menos, o que faço nos meus testes JUnit, onde não quero enfrentar o problema de agrupar minhas exceções verificadas (e de fato preferir que meus testes joguem os originais não desembrulhados)

avandeursen
fonte
16

Essa pergunta pode ser um pouco antiga, mas porque acho que a resposta "certa" aqui é apenas uma maneira de levar a alguns problemas ocultos, problemas mais adiante no seu código. Mesmo se houver um pouco de controvérsia , existem exceções verificadas por um motivo.

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

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

Dependendo do que você realmente deseja obter 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 gostar de nenhuma dessas maneiras, considere usar (dependendo da Exceção original) pelo menos uma exceção.

Mariano
fonte
3
Isso deve ser marcado como uma resposta correta. +. Mas não posso dizer que é um bom post. Você deve elaborar bastante. Como a própria exceção poderia ajudar? Quais são as exceções ocorridas? Por que você não trouxe as diferentes variantes aqui? Ter todo o código de exemplo nas referências é considerado um estilo de postagem ainda proibido aqui no SO. Seu texto parece mais um monte de comentários.
Gangnus
2
Você deve poder escolher em qual nível deseja capturá-los e os fluxos mexerão com isso. A API de fluxo deve permitir que você execute a exceção até a operação final (como coleta) e seja manipulada lá com um manipulador ou lançada de outra forma. stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...). Deve esperar exceções e permitir que você lide com elas como os futuros.
aalku
10

Sugiro usar a classe Google Guava Throwables

propagar ( jogável jogável)

Propaga jogável como está se for uma instância de RuntimeException ou Error, ou, como último recurso, agrupa-o em um RuntimeException e depois 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);
        }
    });
}
yanefedor
fonte
4
Este método foi preterido (infelizmente).
Robert Važan
9

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);
    }
}
aalku
fonte
9

Você pode querer fazer um dos seguintes:

  • propagar exceção verificada,
  • envolva-o e propague a exceção não verificada ou
  • captura a exceção e interrompe a propagação.

Várias bibliotecas permitem fazer isso facilmente. O 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)));
Robert Važan
fonte
Bom trabalho na biblioteca! Eu estava prestes a escrever algo semelhante.
Jasper de Vries
2

Maneira mais legível:

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

Apenas esconda-o RuntimeExceptionpara conseguir passarforEach()

  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 me oponha a alguém se ele disser pular os fluxos e seguir 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()
Kashyap
fonte