Função Java 8 Lambda que lança exceção?

Eu sei como criar uma referência para um método que tem um parâmetro String e retorna um int , é:

 Function 

No entanto, isso não funciona se a function lançar uma exceção, digamos que ela seja definida como:

 Integer myMethod(String s) throws IOException 

Como eu definiria essa referência?

Você precisará seguir um destes procedimentos.

  • Se for o seu código, defina sua própria interface funcional que declara a exceção verificada

     @FunctionalInterface public interface CheckedFunction { R apply(T t) throws IOException; } 

    e usá-lo

     void foo (CheckedFunction f) { ... } 
  • Caso contrário, Integer myMethod(String s) em um método que não declare uma exceção verificada:

     public Integer myWrappedMethod(String s) { try { return myMethod(s); } catch(IOException e) { throw new UncheckedIOException(e); } } 

    e depois

     Function f = (String t) -> myWrappedMethod(t); 

    ou

     Function f = (String t) -> { try { return myMethod(t); } catch(IOException e) { throw new UncheckedIOException(e); } }; 

Você pode estender o Consumer (e Function etc.) com uma nova interface que lida com exceções – usando os methods padrão do Java 8!

Considere essa interface (estende o Consumer ):

 @FunctionalInterface public interface ThrowingConsumer extends Consumer { @Override default void accept(final T elem) { try { acceptThrows(elem); } catch (final Exception e) { // Implement your own exception handling logic here.. // For example: System.out.println("handling an exception..."); // Or ... throw new RuntimeException(e); } } void acceptThrows(T elem) throws Exception; } 

Então, por exemplo, se você tiver uma lista:

 final List list = Arrays.asList("A", "B", "C"); 

Se você quiser consumi-lo (por exemplo, com forEach ) com algum código que gera exceções, você teria tradicionalmente configurado um bloco try / catch:

 final Consumer consumer = aps -> { try { // maybe some other code here... throw new Exception("asdas"); } catch (final Exception ex) { System.out.println("handling an exception..."); } }; list.forEach(consumer); 

Mas com essa nova interface, você pode instanciá-la com uma expressão lambda e o compilador não reclamará:

 final ThrowingConsumer throwingConsumer = aps -> { // maybe some other code here... throw new Exception("asdas"); }; list.forEach(throwingConsumer); 

Ou até mesmo lançar para ser mais sucinto!

 list.forEach((ThrowingConsumer) aps -> { // maybe some other code here... throw new Exception("asda"); }); 

Atualização : Parece que há uma parte muito boa da biblioteca de utilitários do Durian chamada Erros, que pode ser usada para resolver este problema com muito mais flexibilidade. Por exemplo, na minha implementação acima, defini explicitamente a política de tratamento de erros ( System.out... ou throw RuntimeException ), enquanto os Erros do Durian permitem que você aplique uma política throw RuntimeException através de um grande conjunto de methods de utilitário. Obrigado por compartilhar , @NedTwigg !.

Uso da amostra:

 list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c))); 

Acho que a class Errors de Durian combina muitas das vantagens das várias sugestões acima.

  • Envolva uma function de lançamento em uma interface funcional padrão do Java 8.
  • Especifique facilmente várias políticas para lidar com erros
  • Ao encapsular um método que retorna um valor, há uma distinção importante entre especificar um valor padrão ou reexecutar uma RuntimeException.
  • Versões de lançamento das interfaces funcionais do Java 8
    • Semelhante à resposta de fge
  • Interfaces padrão para lançar exceções específicas
    • Que aborda a preocupação de Zoltán

Para include o Durian no seu projeto, você pode:

  • agarrá-lo de jcenter ou maven central em com.diffplug.durian:durian:3.3.0
  • ou apenas copie e cole apenas duas pequenas classs em seu código: Throwing.java e Errors.java

Isso não é específico para o Java 8. Você está tentando compilar algo equivalente a:

 interface I { void m(); } class C implements I { public void m() throws Exception {} //can't compile } 

Disclaimer: Eu não usei o Java 8 ainda, só li sobre isso.

Function não lança IOException , portanto, você não pode colocar qualquer código nele que throws IOException . Se você estiver chamando um método que espera uma Function , o lambda que você passa para esse método não pode lançar IOException , period. Você pode escrever um lambda assim (acho que essa é a syntax lambda, não tenho certeza):

 (String s) -> { try { return myMethod(s); } catch (IOException ex) { throw new RuntimeException(ex); // (Or do something else with it...) } } 

Ou, se o método para o qual você está passando o lambda for um que você mesmo escreveu, você pode definir uma nova interface funcional e usá-la como o tipo de parâmetro em vez de Function :

 public interface FunctionThatThrowsIOException { O apply(I input) throws IOException; } 

Se você não se importa em usar uma biblioteca de terceiros ( Vavr ), você pode escrever

 CheckedFunction1 f = this::myMethod; 

Ele também tem a chamada tentativa monad que lida com erros:

 Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable) .map(i -> ...) // only executed on Success ... 

Por favor, leia mais aqui .

Disclaimer: Eu sou o criador do Vavr.

Você pode usar o invólucro anti-queda

 Function func1 = s -> Unthrow.wrap(() -> myMethod(s)); 

ou

 Function func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1); 

Você pode.

Extending @marcg’s UtilException e adicionando genérico quando necessário: desta forma, o compilador irá forçá-lo novamente a adicionar cláusulas throw e tudo como se você pudesse lançar exceções verificadas nativamente nos streams do java 8.

 public final class LambdaExceptionUtil { @FunctionalInterface public interface Function_WithExceptions { R apply(T t) throws E; } /** * .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) { throwActualException(exception); return null; } }; } @SuppressWarnings("unchecked") private static  void throwActualException(Exception exception) throws E { throw (E) exception; } } public class LambdaExceptionUtilTest { @Test public void testFunction() throws MyTestException { List sizes = Stream.of("ciao", "hello").map(rethrowFunction(s -> transform(s))).collect(toList()); assertEquals(2, sizes.size()); assertEquals(4, sizes.get(0).intValue()); assertEquals(5, sizes.get(1).intValue()); } private Integer transform(String value) throws MyTestException { if(value==null) { throw new MyTestException(); } return value.length(); } private static class MyTestException extends Exception { } } 

Outra solução que usa um wrapper Function seria retornar uma instância de um wrapper de seu resultado, como Success, se tudo correr bem, seja uma instância de, digamos, Failure.

Algum código para esclarecer as coisas:

 public interface ThrowableFunction { B apply(A a) throws Exception; } public abstract class Try { public static boolean isSuccess(Try tryy) { return tryy instanceof Success; } public static  Function> tryOf(ThrowableFunction function) { return a -> { try { B result = function.apply(a); return new Success(result); } catch (Exception e) { return new Failure<>(e); } }; } public abstract boolean isSuccess(); public boolean isError() { return !isSuccess(); } public abstract A getResult(); public abstract Exception getError(); } public class Success extends Try { private final A result; public Success(A result) { this.result = result; } @Override public boolean isSuccess() { return true; } @Override public A getResult() { return result; } @Override public Exception getError() { return new UnsupportedOperationException(); } @Override public boolean equals(Object that) { if(!(that instanceof Success)) { return false; } return Objects.equal(result, ((Success) that).getResult()); } } public class Failure extends Try { private final Exception exception; public Failure(Exception exception) { this.exception = exception; } @Override public boolean isSuccess() { return false; } @Override public A getResult() { throw new UnsupportedOperationException(); } @Override public Exception getError() { return exception; } } 

Um caso de uso simples:

 List> result = Lists.newArrayList(1, 2, 3).stream(). map(Try.tryOf(i -> someMethodThrowingAnException(i))). collect(Collectors.toList()); 

Este problema tem me incomodado também; É por isso que criei este projeto .

Com isso você pode fazer:

 final ThrowingFunction f = yourMethodReferenceHere; 

Há um totla de 39 interfaces definidas pelo JDK que possuem um equivalente Throwing ; esses são todos os @FunctionalInterface s usados ​​em streams (o Stream base, mas também IntStream , LongStream e DoubleStream ).

E como cada um deles estende sua contraparte não jogando, você pode usá-los diretamente em lambdas também:

 myStringStream.map(f) // <-- works 

O comportamento padrão é que quando seu lambda de lançamento lança uma exceção verificada, uma ThrownByLambdaException é lançada com a exceção marcada como a causa. Você pode capturar isso e obter a causa.

Outros resources também estão disponíveis.

Eu tive esse problema com Class.forName e Class.newInstance dentro de um lambda, então eu fiz:

 public Object uncheckedNewInstanceForName (String name) { try { return Class.forName(name).newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } 

Dentro do lambda, em vez de chamar Class.forName (“myClass”). NewInstance () Acabei de chamar uncheckedNewInstanceForName (“myClass”)

Você poderia, no entanto, criar sua própria FunctionalInterface, que é exibida abaixo.

 @FunctionalInterface public interface UseInstance { void accept(T instance) throws X; } 

em seguida, implemente-o usando Lambdas ou referências como mostrado abaixo.

 import java.io.FileWriter; import java.io.IOException; //lambda expressions and the execute around method (EAM) pattern to //manage resources public class FileWriterEAM { private final FileWriter writer; private FileWriterEAM(final String fileName) throws IOException { writer = new FileWriter(fileName); } private void close() throws IOException { System.out.println("close called automatically..."); writer.close(); } public void writeStuff(final String message) throws IOException { writer.write(message); } //... public static void use(final String fileName, final UseInstance block) throws IOException { final FileWriterEAM writerEAM = new FileWriterEAM(fileName); try { block.accept(writerEAM); } finally { writerEAM.close(); } } public static void main(final String[] args) throws IOException { FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet")); FileWriterEAM.use("eam2.txt", writerEAM -> { writerEAM.writeStuff("how"); writerEAM.writeStuff("sweet"); }); FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt); } void writeIt() throws IOException{ this.writeStuff("How "); this.writeStuff("sweet "); this.writeStuff("it is"); } } 

Você pode usar o ET para isso. ET é uma pequena biblioteca Java 8 para conversão / conversão de exceção.

Com o ET, é assim:

 // Do this once ExceptionTranslator et = ET.newConfiguration().done(); ... // if your method returns something Function f = (t) -> et.withReturningTranslation(() -> myMethod(t)); // if your method returns nothing Consumer c = (t) -> et.withTranslation(() -> myMethod(t)); 

ExceptionTranslator instâncias do ExceptionTranslator são seguras para thread e podem ser compartilhadas por vários componentes. Você pode configurar regras de conversão de exceção mais específicas (por exemplo, FooCheckedException -> BarRuntimeException ), se desejar. Se nenhuma outra regra estiver disponível, as exceções verificadas serão convertidas automaticamente em RuntimeException .

(Disclaimer: Eu sou o autor de ET)

Há muitas ótimas respostas já postadas aqui. Apenas tentando resolver o problema com uma perspectiva diferente. É apenas meus 2 centavos, por favor, corrija-me se estou errado em algum lugar.

A cláusula Throws no FunctionalInterface não é uma boa ideia

Eu acho que isso provavelmente não é uma boa idéia para impor throws IOException por causa das seguintes razões

  • Isso me parece um anti-padrão para o Stream / Lambda. A ideia é que o chamador decida qual código fornecer e como lidar com a exceção. Em muitos cenários, a IOException pode não ser aplicável ao cliente. Por exemplo, se o cliente estiver obtendo valor do cache / memory em vez de executar a E / S real.

  • Além disso, o tratamento de exceções em streams torna-se realmente hediondo. Por exemplo, aqui está o meu código será semelhante se eu usar sua API

      acceptMyMethod(s -> { try { Integer i = doSomeOperation(s); return i; } catch (IOException e) { // try catch block because of throws clause // in functional method, even though doSomeOperation // might not be throwing any exception at all. e.printStackTrace(); } return null; }); 

    Feio não é? Além disso, como mencionei em meu primeiro ponto, que o método doSomeOperation pode ou não estar lançando IOException (dependendo da implementação do cliente / chamador), mas por causa da cláusula throws no seu método FunctionalInterface, eu sempre tenho que escrever o tentar pegar.

O que faço se realmente souber que esta API lança IOException

  • Então provavelmente estamos confundindo FunctionalInterface com Interfaces típicas. Se você souber que esta API lançará IOException, provavelmente você também conhece algum comportamento padrão / abstrato. Eu acho que você deve definir uma interface e implantar sua biblioteca (com a implementação padrão / abstrata) da seguinte forma

     public interface MyAmazingAPI { Integer myMethod(String s) throws IOException; } 

    Mas, o problema try-catch ainda existe para o cliente. Se eu usar sua API no stream, ainda precisarei manipular o IOException no horrível bloco try-catch.

  • Forneça uma API padrão amigável ao stream da seguinte maneira

     public interface MyAmazingAPI { Integer myMethod(String s) throws IOException; default Optional myMethod(String s, Consumer exceptionConsumer) { try { return Optional.ofNullable(this.myMethod(s)); } catch (Exception e) { if (exceptionConsumer != null) { exceptionConsumer.accept(e); } else { e.printStackTrace(); } } return Optional.empty(); } } 

    O método padrão considera o object consumidor como argumento, que será responsável por manipular a exceção. Agora, do ponto de vista do cliente, o código ficará assim

     strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace)) .filter(Optional::isPresent) .map(Optional::get).collect(toList()); 

    Nice certo? Obviamente, logger ou outra lógica de manipulação poderia ser usada em vez de Exception :: printStackTrace.

  • Você também pode expor um método semelhante a https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- . Isso significa que você pode expor outro método, que conterá a exceção da chamada de método anterior. A desvantagem é que agora você está tornando suas APIs stateful, o que significa que você precisa lidar com segurança de thread e que eventualmente se tornará um impacto no desempenho. Apenas uma opção a considerar embora.

Crie um tipo de retorno personalizado que propague a exceção verificada. Essa é uma alternativa para criar uma nova interface que espelha a interface funcional existente com a pequena modificação de uma “exceção de lançamento” no método da interface funcional.

Definição

CheckedValuefornecedor

 public static interface CheckedValueSupplier { public V get () throws Exception; } 

CheckedValue

 public class CheckedValue { private final V v; private final Optional opt; public Value (V v) { this.v = v; } public Value (Exception e) { this.opt = Optional.of(e); } public V get () throws Exception { if (opt.isPresent()) { throw opt.get(); } return v; } public Optional getException () { return opt; } public static  CheckedValue returns (T t) { return new CheckedValue(t); } public static  CheckedValue rethrows (Exception e) { return new CheckedValue(e); } public static  CheckedValue from (CheckedValueSupplier sup) { try { return CheckedValue.returns(sup.get()); } catch (Exception e) { return Result.rethrows(e); } } public static  CheckedValue escalates (CheckedValueSupplier sup) { try { return CheckedValue.returns(sup.get()); } catch (Exception e) { throw new RuntimeException(e); } } } 

Uso

 // Don't use this pattern with FileReader, it's meant to be an // example. FileReader is a Closeable resource and as such should // be managed in a try-with-resources block or in another safe // manner that will make sure it is closed properly. // This will not compile as the FileReader constructor throws // an IOException. Function sToFr = (fn) -> new FileReader(Paths.get(fn).toFile()); // Alternative, this will compile. Function> sToFr = (fn) -> { return CheckedValue.from ( () -> new FileReader(Paths.get("/home/" + f).toFile())); }; // Single record usage // The call to get() will propagate the checked exception if it exists. FileReader readMe = pToFr.apply("/home/README").get(); // List of records usage List paths = ...; //a list of paths to files Collection> frs = paths.stream().map(pToFr).collect(Collectors.toList()); // Find out if creation of a file reader failed. boolean anyErrors = frs.stream() .filter(f -> f.getException().isPresent()) .findAny().isPresent(); 

O que está acontecendo?

Adicionar “exceções” a todas as interfaces funcionais no JDK violaria o princípio DRY da maneira mais hedionda. Para evitar isso, é criada uma única interface funcional que gera uma exceção verificada ( CheckedValueSupplier ). Esta será a única interface funcional que permite exceções verificadas. Todas as outras interfaces funcionais aproveitarão o CheckedValueSupplier para envolver qualquer código que lança uma exceção verificada.

A class CheckedValue manterá o resultado da execução de qualquer lógica que lance uma exceção verificada. Isso impede a propagação de uma exceção verificada até o ponto em que o código tenta acessar o valor que uma ocorrência de CheckedValue contém.

Os problemas com essa abordagem.

  • Agora estamos lançando “Exception” efetivamente ocultando o tipo específico originalmente lançado.
  • Não temos conhecimento de que ocorreu uma exceção até que CheckedValue#get() seja chamado.

Consumer et al

Algumas interfaces funcionais ( Consumer por exemplo) devem ser manipuladas de maneira diferente, pois não fornecem um valor de retorno.

Função no lugar do consumidor

Uma abordagem é usar uma function em vez de um consumidor, que se aplica ao manipular streams.

  List lst = Lists.newArrayList(); // won't compile lst.stream().forEach(e -> throwyMethod(e)); // compiles lst.stream() .map(e -> CheckedValueSupplier.from( () -> {throwyMethod(e); return e;})) .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior 

Escalar

Como alternativa, você sempre pode escalar para um RuntimeException . Existem outras respostas que abrangem o escalonamento de uma exceção verificada dentro de um Consumer .

Não consuma.

Apenas evite todas as interfaces funcionais e use um loop de saída.

O idioma de lançamento sorrateiro permite ignorar a CheckedException da expressão do Lambda. Quebra automática de um CheckedException em um RuntimeException não é bom para tratamento de erro estrito.

Pode ser usado como uma function do Consumer usada em uma coleção Java.

Aqui está uma versão simples e melhorada da resposta do jib .

 import static Throwing.rethrow; @Test public void testRethrow() { thrown.expect(IOException.class); thrown.expectMessage("i=3"); Arrays.asList(1, 2, 3).forEach(rethrow(e -> { int i = e.intValue(); if (i == 3) { throw new IOException("i=" + i); } })); } 

Isso apenas envolve o lambda em um relançamento . Faz CheckedException relançar qualquer Exception que foi lançada no seu lambda.

 public final class Throwing { private Throwing() {} @Nonnull public static  Consumer rethrow(@Nonnull final ThrowingConsumer consumer) { return consumer; } /** * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it * allows the unchecked exception to propagate. * * http://www.baeldung.com/java-sneaky-throws */ @SuppressWarnings("unchecked") @Nonnull public static  void sneakyThrow(@Nonnull Throwable ex) throws E { throw (E) ex; } } 

Encontre um código completo e testes unitários aqui .

Se você não se importar em usar uma biblioteca de terceiros, com cyclops-react , uma biblioteca com a qual eu contribuir, você pode usar a API FluentFunctions para escrever

  Function standardFn = FluentFunctions.ofChecked(this::myMethod); 

ofChecked pega um jOOλ CheckedFunction e retorna a referência suavizada de volta a um java.util.function.Function JDK padrão (não verificado).

Alternativamente, você pode continuar trabalhando com a function capturada através da API FluentFunctions!

Por exemplo, para executar seu método, repetindo-o até 5 vezes e registrando seu status, você pode escrever

  FluentFunctions.ofChecked(this::myMethod) .log(s->log.debug(s),e->log.error(e,e.getMessage()) .try(5,1000) .apply("my param"); 

Por padrão, o Java 8 Function não permite lançar exceção e, como sugerido em várias respostas, há muitas maneiras de alcançá-lo, uma delas é:

 @FunctionalInterface public interface FunctionWithException { R apply(T t) throws E; } 

Defina como:

 private FunctionWithException myMethod = (str) -> { if ("abc".equals(str)) { throw new IOException(); } return 1; }; 

E adicione throws ou try/catch a mesma exceção no método do chamador.

O que estou fazendo é permitir que o usuário forneça o valor que ele realmente deseja em caso de exceção. Então, eu tenho algo parecido com isso

 public static  Function defaultIfThrows(FunctionThatThrows delegate, R defaultValue) { return x -> { try { return delegate.apply(x); } catch (Throwable throwable) { return defaultValue; } }; } @FunctionalInterface public interface FunctionThatThrows { R apply(T t) throws Throwable; } 

E isso pode ser chamado como:

 defaultIfThrows(child -> child.getID(), null) 

Várias das soluções oferecidas usam um argumento genérico de E para passar o tipo da exceção que é lançada.

Leve isso um passo adiante, e em vez de passar o tipo da exceção, passe em um Consumidor do tipo de exceção, como em …

 Consumer 

Você pode criar várias variações reutilizáveis ​​do Consumer do Consumer quais cobririam as necessidades comuns de exception handling de seu aplicativo.

Eu farei algo genérico:

 public interface Lambda { @FunctionalInterface public interface CheckedFunction { T get() throws Exception; } public static  T handle(CheckedFunction supplier) { try { return supplier.get(); } catch (Exception exception) { throw new RuntimeException(exception); } } } 

uso:

  Lambda.handle(() -> method()); 

Use Jool Library ou diga jOOλ library JOOQ de JOOQ . Ele não apenas fornece interfaces manipuladas de exceção não verificadas, mas também fornece à class Seq muitos methods úteis.

Além disso, contém Interfaces Funcionais com até 16 parâmetros. Além disso, fornece class Tuple que é usada em diferentes cenários.

Jool Git Link

Especificamente na pesquisa de biblioteca para o pacote org.jooq.lambda.fi.util.function . Ele contém todas as interfaces do Java-8 com o Checked prefixado. Veja abaixo para referência: –

insira a descrição da imagem aqui

 public void frankTest() { int pageId= -1; List users= null; try { //Does Not Compile: Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name"))); //Compiles: Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { try { final Book bk= users.stream().filter(bp -> { String name= null; try { name = rw.getString("name"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return bp.getTitle().equals(name); }).limit(1).collect(Collectors.toList()).get(0); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); } ); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }