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?



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).


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)


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.


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

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

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

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()
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.