Função Java 8 Lambda que gera exceção?

469

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

Function<String, Integer>

No entanto, isso não funciona se a função gerar uma exceção, digamos que seja definida como:

Integer myMethod(String s) throws IOException

Como eu definiria essa referência?

Triton Man
fonte
1
Veja também: stackoverflow.com/questions/31637892/…
Marko Topolnik
1
... e este: stackoverflow.com/questions/31270759/…
Vadzim 16/15
github.com/TouK/ThrowingFunction
Grzegorz Piwowarek 04/04
4
Toda a solução parece com um pouco de como, lançando exceções de tempo de execução, acredito que não é uma boa solução. então é melhor usar java velho para loops
Nazeel
5
E a biblioteca jool ? pacote cf org.jooq.lambda.Unchecked
chaiyachaiya

Respostas:

403

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<T, R> {
       R apply(T t) throws IOException;
    }

    e use-o:

    void foo (CheckedFunction f) { ... }
  • Caso contrário, envolva Integer myMethod(String s)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<String, Integer> f = (String t) -> myWrappedMethod(t);

    ou:

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };
Jason
fonte
7
Você pode realmente estender Consumerou Functionse usar métodos padrão - veja minha resposta abaixo.
JLB
2
Eu acho que isso pode ser feito como uma linha .
Ned Twigg
6
Otimização menor: em vez de (String t) -> myWrappedMethod(t), a referência do método this::myWrappedMethodtambém pode ser usada.
Clashsoft
8
Uma maneira ainda mais genérica de fazer isso é definir a função marcada como esta interface pública @FunctionalInterface CheckedFunction <T, R, E estende a exceção> {R apply (T t) lança E; } Dessa forma, você também pode definir qual exceção a função está lançando e pode reutilizar a interface para qualquer código.
Martin Odhelius 9/09/16
3
Uau. Java é pior do que eu pensava
user275801 09/01
194

Você pode realmente estender Consumer(e Functionetc.) com uma nova interface que lida com exceções - usando os métodos padrão do Java 8 !

Considere esta interface (estende Consumer):

@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {

    @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<String> list = Arrays.asList("A", "B", "C");

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

final Consumer<String> 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 instancia-la com uma expressão lambda e o compilador não irá reclamar:

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

Ou até mesmo faça o elenco para ser mais sucinto !:

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

Atualização : Parece que há uma parte muito agradável da biblioteca de utilitários do Durian chamada Erros, que pode ser usada para resolver esse 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 aplicar uma política em tempo real por meio de um amplo conjunto de métodos utilitários. Obrigado por compartilhar , @NedTwigg !.

Uso da amostra:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));
jlb
fonte
14
Portanto, você tem um conjunto de interfaces (Função, Consumidor, Fornecedor, ...) e um conjunto de políticas para lidar com erros (Throwing, System.out.println, ...). Eu acho que existe uma maneira de facilitar o uso de qualquer política com qualquer tipo de função, sem a necessidade de copiar e colar "ThrowingConsumer, ThrowingFunction, etc.".
Ned Twigg
1
algum tempo depois ... eu decidi usar exceções não verificadas e não usar interfaces funcionais adicionais ou novas bibliotecas -> o caminho fácil, menos digitação, entrega mais rápida, não é?
aliopi
1
Aqui está uma versão aprimorada usando o idioma de lançamento sorrateiro. Não há necessidade de desembrulhar RuntimeException em CheckException.
myui
62

Acho que a Errorsaula de Durian combina muitos dos profissionais das várias sugestões acima.

Para incluir o Durian em seu projeto, você pode:

Ned Twigg
fonte
Ou você pode simplesmente usar o RxJava, pois os fluxos precisam de tratamento de erros inerente e, se houver algo em seu pipeline que gera uma exceção, há uma boa chance de que seja provavelmente um fluxo observável. Isso também não força o Java 8 nos consumidores downstream de uma biblioteca.
Adam Gent
2
Observe que o Durian não tem novas versões desde junho de 2016. Não é uma parada para shows, mas algo para se lembrar.
Istvan Devai
9
Mantian Durian aqui. O que está quebrado? Se um usuário encontrar um bug ou um recurso ausente importante, lançaremos uma correção rapidamente. A biblioteca é simples, portanto, não tivemos nenhum relatório de bug; portanto, não precisamos liberar nenhuma correção.
Ned Twigg
28

Isso não é específico do 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
}
assilias
fonte
15
A pergunta é "Como eu definiria essa referência?" . Na verdade, isso não responde à pergunta; apenas esclarece qual é o problema.
Dawood ibn Kareem
13

Isenção de responsabilidade: ainda não usei o Java 8, apenas li sobre ele.

Function<String, Integer>não joga IOException, então você não pode colocar nenhum código nele throws IOException. Se você está chamando um método que espera a Function<String, Integer>, então o lambda que você passa para esse método não pode lançar IOException, ponto final. Você pode escrever um lambda como este (acho que essa é a sintaxe 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 é aquele que você mesmo escreveu, você pode definir uma nova interface funcional e usá-la como o tipo de parâmetro em vez de Function<String, Integer>:

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}
Adam R. Nelson
fonte
adicione a anotação @FunctionalInterface antes da sua interface, somente assim ela poderá ser usada para lambdas.
Gangnus
13
@ Gangnus: a @FunctionalInterfaceanotação não é necessária para que seja utilizável para lambdas. É recomendado para verificação de sanidade embora.
precisa
9

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

CheckedFunction1<String, Integer> f = this::myMethod;

Ele também possui a chamada try 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.

Daniel Dietrich
fonte
7

Você pode usar invólucro unthrow

Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));

ou

Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);
SeregaLBN
fonte
6

No entanto, você pode criar seu próprio FunctionalInterface que é exibido como abaixo.

@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
  void accept(T instance) throws X;
}

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<FileWriterEAM, IOException> 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");

 }

}
JohnnyO
fonte
6

Você pode.

Estendendo @marcg 's UtilExceptione adicionando genéricos sempre <E extends Exception>que necessário: dessa forma, o compilador forçará novamente a adicionar cláusulas throw e tudo mais como se você pudesse lançar exceções verificadas nativamente nos fluxos do java 8.

public final class LambdaExceptionUtil {

    @FunctionalInterface
    public interface Function_WithExceptions<T, R, E extends Exception> {
        R apply(T t) throws E;
    }

    /**
     * .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName))
     */
    public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E  {
        return t -> {
            try {
                return function.apply(t);
            } catch (Exception exception) {
                throwActualException(exception);
                return null;
            }
        };
    }

    @SuppressWarnings("unchecked")
    private static <E extends Exception> void throwActualException(Exception exception) throws E {
        throw (E) exception;
    }

}

public class LambdaExceptionUtilTest {

    @Test
    public void testFunction() throws MyTestException {
        List<Integer> sizes = Stream.of("ciao", "hello").<Integer>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 { }
}
PaoloC
fonte
5

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

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

Sergio
fonte
4

Outra solução usando um wrapper Function seria retornar uma instância de um wrapper do seu resultado, por exemplo, Sucesso, se tudo corresse bem, ou uma instância de, por exemplo, Failure.

Algum código para esclarecer as coisas:

public interface ThrowableFunction<A, B> {
    B apply(A a) throws Exception;
}

public abstract class Try<A> {

    public static boolean isSuccess(Try tryy) {
        return tryy instanceof Success;
    }

    public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
        return a -> {
            try {
                B result = function.apply(a);
                return new Success<B>(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<A> extends Try<A> {

    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<A> extends Try<A> {

    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<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
    map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
    collect(Collectors.toList());
yohan
fonte
4

Esse problema também está me incomodando; é por isso que eu criei este projeto .

Com ele você pode fazer:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

Há uma totla de 39 interfaces definidas pelo JDK que possuem esse Throwingequivalente; aqueles são todos @FunctionalInterfaces utilizados em correntes (a base Stream, mas também IntStream, LongStreame DoubleStream).

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

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

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

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

fge
fonte
Eu realmente gosto da idéia, gostaria que você tornasse as jogáveis ​​genéricas, conforme sugerido aqui: javaspecialists.eu/archive/Issue221.html , por exemplo: @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }- dessa maneira, o usuário não precisa capturar Throwable, mas a exceção específica verificada.
Zoltán
@ Zoltán que seria difícil declarar a exceção todas as vezes; Além disso, você sempre pode apenas usar, digamos, .Aplique () em vez de .doApply () e pegar ThrownByLambdaException, você terá a exceção original como causa (ou você pode usar rethrow(...).as(MyRuntimeException.class))
fge
Eu acho que existe (meio que) uma maneira de contornar isso .
Ned Twigg
@NedTwigg Eu também resolvi isso há muito tempo; Agora posso usar Throwing.runnable()e outros, sempre com recursos de encadeamento
fge 14/05
A funcionalidade de encadeamento é muito legal! Meu comentário foi sobre se ThrowingRunnable deve ter a exceção genérica ou não. Zoltan perguntou se a sua biblioteca poderia ter o argumento como um parâmetro genérico, e você disse que seria muito difícil de usar. Meu link foi para algumas linhas de código que mostram uma maneira de as exceções serem genéricas, sem que isso seja um problema. A menos que eu o tenha interpretado mal, as exceções na sua biblioteca não são genéricas (que é uma opção de design razoável, porque você não obtém muita utilidade ao torná-las genéricas).
Ned Twigg
4

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

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

Acho que isso provavelmente não é uma boa ideia para aplicar lances IOException por causa dos seguintes motivos

  • Isso me parece um anti-padrão para o Stream / Lambda. A idéia é 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 / memória em vez de executar a E / S real.

  • Além disso, as exceções de manipulação nos fluxos se tornam realmente hediondas. Por exemplo, aqui está o meu código, 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 no meu primeiro ponto, que o método doSomeOperation pode ou não estar lançando IOException (dependendo da implementação do cliente / responsável pela chamada), mas devido à cláusula throws no seu método FunctionalInterface, sempre preciso 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 essa API lançará IOException, provavelmente também conhecerá algum comportamento padrão / abstrato também. Eu acho que você deve definir uma interface e implantar sua biblioteca (com implementação padrão / abstrata) da seguinte maneira

    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 fluxo, ainda preciso lidar com IOException no hediondo bloco try-catch.

  • Forneça uma API padrão compatível com o fluxo da seguinte maneira

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    
        default Optional<Integer> myMethod(String s, Consumer<? super Exception> 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 usa o objeto consumidor como argumento, que será responsável por lidar com 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 né? Obviamente, o logger ou outra lógica de manipulação pode 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- . Significando 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 com estado, o que significa que você precisa lidar com a segurança de threads e que acabará se tornando um problema de desempenho. Apenas uma opção a considerar.

TriCore
fonte
Concordo que converter a exceção verificada em uma exceção não verificada ou engolir a exceção não é uma boa idéia, porque não há como saber qual elemento da Streamexceção gerada foi lançada. Portanto, gosto da ideia de ter um manipulador de exceções e filtrar os resultados que não são válidos. Observe que o MyAmazingAPI é efetivamente um FunctionalInterface(portanto, você pode adicionar a anotação @FunctionalInterface). Além disso, você pode ter um valor padrão em vez de usar Optional.empty().
Julien Kronegg
4

O idioma sneaky throw permite ignorar a CheckedExceptionexpressão Lambda. O agrupamento de um CheckedExceptionem a RuntimeExceptionnão é bom para o tratamento estrito de erros.

Pode ser usado como uma Consumerfunção usada em uma coleção Java.

Aqui está uma versão simples e aprimorada 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 uma repetição . Torna o CheckedExceptionrelançamento qualquer Exceptionque foi lançado em sua lambda.

public final class Throwing {
    private Throwing() {}

    @Nonnull
    public static <T> Consumer<T> rethrow(@Nonnull final ThrowingConsumer<T> 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 <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E {
        throw (E) ex;
    }

}

Encontre um código completo e testes de unidade aqui .

myui
fonte
3

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

Com o ET, fica assim:

// Do this once
ExceptionTranslator et = ET.newConfiguration().done();

...

// if your method returns something
Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));

// if your method returns nothing
Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));

ExceptionTranslatorinstâncias são thread-safe 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 marcadas serão automaticamente convertidas em RuntimeException.

(Aviso: sou o autor do ET)

micha
fonte
2
Parece que você é o autor desta biblioteca. De acordo com as regras da SO , você deve divulgar sua afiliação em suas respostas. Adicione explicitamente à sua resposta que você escreveu esta biblioteca (o mesmo para outras respostas relacionadas ao ET).
Tagir Valeev
2
Oi Tagir, obrigado pela dica. Eu atualizei a resposta.
micha
2

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

public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
    return x -> {
        try {
            return delegate.apply(x);
        } catch (Throwable throwable) {
            return defaultValue;
        }
    };
}

@FunctionalInterface
public interface FunctionThatThrows<T, R> {
    R apply(T t) throws Throwable;
}

E isso pode ser chamado como:

defaultIfThrows(child -> child.getID(), null)
mmounirou
fonte
1
Essa é uma extensão dessa ideia que faz uma distinção entre uma estratégia de "valor padrão" (como na sua resposta) e uma estratégia de "repetir novamente RuntimeException", em que um valor padrão não é necessário.
Ned Twigg
2

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

 Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

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

Como alternativa, você pode continuar trabalhando com a função capturada através da API FluentFunctions!

Por exemplo, para executar seu método, tente novamente 5 vezes e registre o status que você pode escrever

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

Por padrão, o Java 8 Function não permite gerar exceções e, como sugerido em várias respostas, existem várias maneiras de alcançá-lo, uma maneira é:

@FunctionalInterface
public interface FunctionWithException<T, R, E extends Exception> {
    R apply(T t) throws E;
}

Defina como:

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

E adicione throwsou try/catcha mesma exceção no método de chamada.

Arpit Aggarwal
fonte
2

Crie um tipo de retorno personalizado que irá propagar a exceção verificada. Essa é uma alternativa à criação de uma nova interface que espelha a interface funcional existente com a leve modificação de uma "exceção de lançamento" no método da interface funcional.

Definição

CheckedValueSupplier

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

CheckedValue

public class CheckedValue<V> {
    private final V v;
    private final Optional<Exception> 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<Exception> getException () {
        return opt;
    }

    public static <T> CheckedValue<T> returns (T t) {
        return new CheckedValue<T>(t);
    }

    public static <T> CheckedValue<T> rethrows (Exception e) {
        return new CheckedValue<T>(e);
    }

    public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            return Result.rethrows(e);
        }
    }

    public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> 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<String, FileReader> sToFr =
        (fn) -> new FileReader(Paths.get(fn).toFile());

// Alternative, this will compile.
    Function<String, CheckedValue<FileReader>> 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<String> paths = ...; //a list of paths to files
    Collection<CheckedValue<FileReader>> 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?

Uma única interface funcional que gera uma exceção verificada é criada ( CheckedValueSupplier). Essa será a única interface funcional que permite exceções verificadas. Todas as outras interfaces funcionais aproveitarão CheckedValueSupplierpara envolver qualquer código que lança uma exceção verificada.

A CheckedValueclasse manterá o resultado da execução de qualquer lógica que lança uma exceção verificada. Isso evita a propagação de uma exceção verificada até o ponto em que o código tenta acessar o valor que uma instância CheckedValuecontém.

Os problemas com essa abordagem.

  • Agora estamos lançando "Exception" efetivamente ocultando o tipo específico originalmente lançado.
  • Não sabemos que ocorreu uma exceção até que CheckedValue#get()seja chamada.

Consumer et al

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

Função em vez de Consumidor

Uma abordagem é usar uma função em vez de um consumidor, que se aplica ao manipular fluxos.

    List<String> 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 a RuntimeException. Há outras respostas que cobrem a escalação de uma exceção verificada de dentro de a Consumer.

Não consuma.

Evite todas as interfaces funcionais e use um loop for de boa forma.

justin.hughey
fonte
2

Eu uso uma função de utilitário sobrecarregada chamada unchecked()que lida com vários casos de uso.


Alguns exemplos de usos

unchecked(() -> new File("hello.txt").createNewFile());

boolean fileWasCreated = unchecked(() -> new File("hello.txt").createNewFile());

myFiles.forEach(unchecked(file -> new File(file.path).createNewFile()));

UTILIDADES DE APOIO

public class UncheckedUtils {

    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingSupplier<T> {
        T get() throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Exception;
    }

    public static <T> Consumer<T> unchecked(
            ThrowingConsumer<T> throwingConsumer
    ) {
        return i -> {
            try {
                throwingConsumer.accept(i);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };
    }

    public static <T> T unchecked(
            ThrowingSupplier<T> throwingSupplier
    ) {
        try {
            return throwingSupplier.get();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void unchecked(
            ThrowingRunnable throwing
    ) {
        try {
            throwing.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}
Stephen Paul
fonte
0

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

Dê um passo adiante e, em vez de passar o tipo de exceção, passe um Consumidor do tipo de exceção, como em ...

Consumer<E extends Exception>

Você pode criar várias variações reutilizáveis, as Consumer<Exception>quais cobririam as necessidades comuns de tratamento de exceções do seu aplicativo.

Rodney P. Barbati
fonte
0

Vou fazer algo genérico:

public interface Lambda {

    @FunctionalInterface
    public interface CheckedFunction<T> {

        T get() throws Exception;
    }

    public static <T> T handle(CheckedFunction<T> supplier) {
        try {
            return supplier.get();
        } catch (Exception exception) {
            throw new RuntimeException(exception);

        }
    }
}

uso:

 Lambda.handle(() -> method());
ahll
fonte
0

Use Jool Libraryou dizer jOOλ librarya partir JOOQ. Ele não apenas fornece interfaces manipuladas de exceção não verificadas, mas também fornece à classe Seq muitos métodos úteis.

Além disso, ele contém interfaces funcionais com até 16 parâmetros. Além disso, fornece a classe Tuple, usada em diferentes cenários.

Jool Git Link

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

insira a descrição da imagem aqui

Vinay Prajapati
fonte
0

Eu sou o autor de uma pequena biblioteca com alguma mágica genérica para lançar qualquer exceção Java em qualquer lugar, sem a necessidade de capturá-las nem envolvê-lasRuntimeException .

Uso: unchecked(() -> methodThrowingCheckedException())

public class UncheckedExceptions {

    /**
     * throws {@code exception} as unchecked exception, without wrapping exception.
     *
     * @return will never return anything, return type is set to {@code exception} only to be able to write <code>throw unchecked(exception)</code>
     * @throws T {@code exception} as unchecked exception
     */
    @SuppressWarnings("unchecked")
    public static <T extends Throwable> T unchecked(Exception exception) throws T {
        throw (T) exception;
    }


    @FunctionalInterface
    public interface UncheckedFunction<R> {
        R call() throws Exception;
    }

    /**
     * Executes given function,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @return result of function
     * @see #unchecked(Exception)
     */
    public static <R> R unchecked(UncheckedFunction<R> function) {
        try {
            return function.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }


    @FunctionalInterface
    public interface UncheckedMethod {
        void call() throws Exception;
    }

    /**
     * Executes given method,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @see #unchecked(Exception)
     */
    public static void unchecked(UncheckedMethod method) {
        try {
            method.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }
}

fonte: https://github.com/qoomon/unchecked-exceptions-java

qoomon
fonte
-7
public void frankTest() {
    int pageId= -1;

    List<Book> 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();
    }
}
Franky Knuckels
fonte
3
Importa-se de comentar o seu trabalho? Respostas apenas de código não são tão úteis.
Phantômaxx
@Franky você pode corrigir a sua apresentação pelo espaçamento utilização 4, em vez de <code>/<code>:)
AdrieanKhisbe