Funzione Java 8 Lambda che genera un'eccezione?


470

So come creare un riferimento a un metodo che ha un Stringparametro e restituisce un int, è:

Function<String, Integer>

Tuttavia, questo non funziona se la funzione genera un'eccezione, ad esempio è definita come:

Integer myMethod(String s) throws IOException

Come definirei questo riferimento?





4
Tutta la soluzione sembra un po 'come, lanciando eccezioni di runtime, credo che non sia una buona soluzione. quindi meglio usare il vecchio java per i loop
Nazeel

5
Che dire della libreria jool ? cf org.jooq.lambda.Pacchetto non controllato
chaiyachaiya

Risposte:


403

Dovrai eseguire una delle seguenti operazioni.

  • Se è il tuo codice, allora definisci la tua interfaccia funzionale che dichiara l'eccezione selezionata:

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

    e usalo:

    void foo (CheckedFunction f) { ... }
  • Altrimenti, avvolgi Integer myMethod(String s)un metodo che non dichiari un'eccezione controllata:

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

    e poi:

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);

    o:

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

7
Puoi effettivamente estendere Consumero Functionse usi metodi predefiniti - vedi la mia risposta di seguito.
jlb

2
Penso che questo possa essere realizzato come una linea .
Ned Twigg,

6
Ottimizzazione minore: (String t) -> myWrappedMethod(t)al posto di è this::myWrappedMethodpossibile utilizzare anche il riferimento al metodo .
Clashsoft,

8
Un modo ancora più generico per farlo è definire la funzione selezionata come questa interfaccia pubblica @FunctionalInterface CheckedFunction <T, R, E estende Eccezione> {R apply (T t) genera E; } In tal modo è anche possibile definire quale eccezione viene generata dalla funzione e riutilizzare l'interfaccia per qualsiasi codice.
Martin Odhelius,

3
Wow. Java è peggio di quanto pensassi
user275801

194

Puoi effettivamente estendere Consumer(ed Functionecc.) Con una nuova interfaccia che gestisce le eccezioni, usando i metodi predefiniti di Java 8 !

Considera questa interfaccia (estesa 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;

}

Quindi, ad esempio, se hai un elenco:

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

Se vuoi consumarlo (ad es. Con forEach) con un codice che genera eccezioni, tradizionalmente avresti impostato un blocco 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);

Ma con questa nuova interfaccia, puoi istanziarla con un'espressione lambda e il compilatore non si lamenterà:

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

O anche solo lanciarlo per essere più conciso !:

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

Aggiornamento : sembra che ci sia una parte molto bella della libreria di utilità di Durian chiamata Errori che può essere usata per risolvere questo problema con molta più flessibilità. Ad esempio, nella mia implementazione sopra ho definito esplicitamente la politica di gestione degli errori ( System.out...o throw RuntimeException), mentre gli errori di Durian ti consentono di applicare una politica al volo tramite una vasta suite di metodi di utilità. Grazie per averlo condiviso , @NedTwigg !.

Esempio di utilizzo:

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

14
Quindi hai una serie di interfacce (Funzione, Consumatore, Fornitore, ...) e una serie di politiche per la gestione degli errori (Lancio, System.out.println, ...). Penso che ci sia un modo per semplificare l'uso di qualsiasi politica con qualsiasi tipo di funzione, senza dover copiare incolla "ThrowingConsumer, ThrowingFunction, ecc.".
Ned Twigg,

1
qualche tempo dopo ... Ho deciso di utilizzare eccezioni non selezionate e di non utilizzare ulteriori interfacce funzionali o nuove librerie -> la strada facile, meno battitura, consegna più veloce, non è vero.
aliopi,

1
Ecco una versione migliorata che usa un linguaggio di lancio subdolo. Non è necessario decomprimere RuntimeException in CheckException.
myui,

62

Penso che la Errorsclasse di Durian combini molti dei pro dei vari suggerimenti di cui sopra.

Per includere Durian nel tuo progetto, puoi:


Oppure puoi semplicemente usare RxJava poiché i flussi hanno bisogno di una gestione degli errori intrinseca e se c'è qualcosa nella tua pipeline che genera un'eccezione c'è una buona probabilità che sia probabilmente un flusso osservabile. Anche questo non impone Java 8 agli utenti a valle di una libreria.
Adam Gent,

2
Si prega di notare che Durian non ha nuove versioni da giugno 2016. Non è un fermo spettacolo, ma qualcosa da tenere a mente.
Istvan Devai,

9
Manutentore Durian qui. Che cosa è rotto? Se un utente rileva un bug o un'importante funzionalità mancante, rilasciamo rapidamente una correzione. La libreria è semplice, quindi non abbiamo avuto segnalazioni di bug, quindi non abbiamo avuto bisogno di rilasciare correzioni di bug.
Ned Twigg,

28

Questo non è specifico di Java 8. Stai provando a compilare qualcosa di equivalente a:

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

15
La domanda è "Come definirei questo riferimento?" . Questo in realtà non risponde alla domanda; chiarisce solo qual è il problema.
Dawood ibn Kareem,

13

Disclaimer: non ho ancora usato Java 8, ho letto solo su di esso.

Function<String, Integer>non viene lanciato IOException, quindi non è possibile inserire alcun codice throws IOException. Se stai chiamando un metodo che prevede un Function<String, Integer>, quindi il lambda che passi a quel metodo non può lanciare IOException, punto. Puoi scrivere una lambda in questo modo (penso che questa sia la sintassi lambda, non sono sicuro):

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

Oppure, se il metodo a cui stai passando il lambda è uno che hai scritto tu stesso, puoi definire una nuova interfaccia funzionale e usarla come tipo di parametro invece di Function<String, Integer>:

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

aggiungi l'annotazione @FunctionalInterface prima della tua interfaccia, solo allora sarà utilizzabile per lambdas.
Gangnus,

13
@Gangnus: l' @FunctionalInterfaceannotazione non è necessaria per essere utilizzabile per lambdas. Si consiglia comunque di controllare la sanità mentale.
Tanmay Patil,

9

Se non ti dispiace usare una libreria di terze parti ( Vavr ), potresti scrivere

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

Ha anche la cosiddetta monade Try che gestisce gli errori:

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

Per favore leggi di più qui .

Disclaimer: sono il creatore di Vavr.


7

È possibile utilizzare il wrapper Unthrow

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

o

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

6

Puoi comunque creare la tua FunctionalInterface che getta come di seguito.

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

quindi implementalo usando Lambdas o riferimenti come mostrato di seguito.

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

 }

}

6

Puoi.

Estensione di @marcg UtilExceptione aggiunta di generici <E extends Exception>dove necessario: in questo modo, il compilatore ti costringerà di nuovo ad aggiungere clausole di lancio e tutto come se potessi lanciare nativamente le eccezioni verificate sui flussi di 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 { }
}

5

Ho avuto questo problema con Class.forName e Class.newInstance all'interno di una lambda, quindi ho appena fatto:

public Object uncheckedNewInstanceForName (String name) {

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

All'interno della lambda, invece di chiamare Class.forName ("myClass"). NewInstance () ho appena chiamato uncheckedNewInstanceForName ("myClass")


4

Un'altra soluzione che utilizza un wrapper Function sarebbe quella di restituire un'istanza di un wrapper del risultato, ad esempio Success, se tutto è andato bene, o un'istanza di, dire Failure.

Alcuni codici per chiarire le cose:

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

Un semplice caso d'uso:

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

4

Anche questo problema mi ha infastidito; ecco perché ho creato questo progetto .

Con esso puoi fare:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

Esistono un totale di 39 interfacce definite dal JDK che hanno un tale Throwingequivalente; quelli sono tutti @FunctionalInterfaceusati nei flussi (la base Streamma anche IntStream, LongStreameDoubleStream ).

E poiché ciascuno di essi estende la propria controparte non gettante, puoi usarli direttamente anche in lambda:

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

Il comportamento predefinito è che quando il lancio di lambda genera un'eccezione controllata, ThrownByLambdaExceptionviene generata un'eccezione controllata come causa. Puoi quindi catturarlo e ottenere la causa.

Sono disponibili anche altre funzionalità.


Mi piace davvero l'idea, vorrei solo che tu rendessi generici i lanciatori come suggerito qui: javaspecialists.eu/archive/Issue221.html , ad esempio: @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }- in questo modo l'utente non ha bisogno di catturare Throwable, ma l'eccezione specifica selezionata.
Zoltán,

@ Zoltán sarebbe un dolore dichiarare l'eccezione ogni volta; inoltre, puoi sempre usare, diciamo, .apply () invece di .doApply () e catturare ThrownByLambdaException, avrai l'eccezione originale come causa (o puoi usare rethrow(...).as(MyRuntimeException.class))
fge


@NedTwigg L'ho risolto molto tempo fa; Ora posso usare Throwing.runnable()e altri, sempre con capacità di concatenamento
fge

La funzionalità di concatenamento è molto interessante! Il mio commento riguardava se ThrowingRunnable dovrebbe avere o meno l'eccezione generica. Zoltan ha chiesto se la tua libreria potrebbe avere l'argomento come parametro generico, e hai detto che sarebbe stato un problema da usare. Il mio collegamento era con alcune righe di codice che mostrano un modo per fare in modo che le eccezioni siano generiche, senza che sia un problema. A meno che non lo abbia letto male, le eccezioni nella tua libreria non sono generiche (che è una scelta ragionevole di progettazione, perché non ottieni molta utilità rendendole generiche).
Ned Twigg,

4

Ci sono molte ottime risposte già pubblicate qui. Sto solo cercando di risolvere il problema con una prospettiva diversa. Sono solo i miei 2 centesimi, per favore correggimi se sbaglio da qualche parte.

La clausola di lancio in FunctionalInterface non è una buona idea

Penso che questa non sia una buona idea per imporre IOException per i seguenti motivi

  • Questo mi sembra un anti-pattern a Stream / Lambda. L'idea è che il chiamante deciderà quale codice fornire e come gestire l'eccezione. In molti scenari, IOException potrebbe non essere applicabile per il client. Ad esempio, se il client ottiene valore dalla cache / memoria anziché eseguire l'I / O effettivo.

  • Inoltre, la gestione delle eccezioni nei flussi diventa davvero orribile. Ad esempio, ecco il mio codice sarà simile se uso la tua 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;
                });

    Brutto vero? Inoltre, come ho già detto nel mio primo punto, che il metodo doSomeOperation potrebbe o meno generare IOException (a seconda dell'implementazione del client / chiamante), ma a causa della clausola di lancio nel metodo FunctionalInterface, devo sempre scrivere il prova a prendere.

Cosa devo fare se so davvero che questa API genera IOException

  • Quindi probabilmente stiamo confondendo l'interfaccia funzionale con interfacce tipiche. Se sai che questa API genererà IOException, molto probabilmente conosci anche un comportamento predefinito / astratto. Penso che dovresti definire un'interfaccia e distribuire la tua libreria (con l'implementazione predefinita / astratta) come segue

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

    Tuttavia, il problema try-catch esiste ancora per il client. Se utilizzo la tua API in streaming, devo comunque gestire IOException in un orribile blocco try-catch.

  • Fornire un'API di default per lo streaming come segue

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

    Il metodo predefinito utilizza l'oggetto consumer come argomento, che sarà responsabile della gestione dell'eccezione. Ora, dal punto di vista del cliente, il codice sarà simile a questo

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

    Bello vero? Naturalmente, al posto di Exception :: printStackTrace è possibile utilizzare logger o altra logica di gestione.

  • Puoi anche esporre un metodo simile a https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- . Ciò significa che è possibile esporre un altro metodo, che conterrà l'eccezione dalla precedente chiamata al metodo. Lo svantaggio è che stai rendendo le tue API stateful, il che significa che devi gestire la sicurezza dei thread e che alla fine diventerà un successo per le prestazioni. Solo un'opzione da considerare però.


Sono d'accordo che convertire l'eccezione selezionata in un'eccezione non selezionata o ingoiare l'eccezione non è una buona idea perché non c'è modo di sapere quale elemento Streamdell'eccezione sollevata. Pertanto, mi piace l'idea di avere un gestore di eccezioni e di filtrare i risultati non validi. Nota che MyAmazingAPI è effettivamente un FunctionalInterface(quindi potresti aggiungere l'annotazione @FunctionalInterface). Inoltre potresti avere un valore predefinito invece di usare Optional.empty().
Julien Kronegg,

4

Idioma subdolo consente di aggirare l' CheckedExceptionespressione Lambda. L'avvolgimento di a CheckedExceptionin a RuntimeExceptionnon è utile per la gestione rigorosa degli errori.

Può essere utilizzato come una Consumerfunzione utilizzata in una raccolta Java.

Ecco una versione semplice e migliorata della risposta del braccio .

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

Questo avvolge appena la lambda in un ripensamento . Fa CheckedExceptionricominciare tutto Exceptionciò che è stato gettato nella tua 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;
    }

}

Trova un codice completo e unit test qui .


3

Puoi usare ET per questo. ET è una piccola libreria Java 8 per la conversione / traduzione delle eccezioni.

Con ET sembra così:

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

ExceptionTranslatorle istanze sono thread-safe e possono essere condivise da più componenti. FooCheckedException -> BarRuntimeExceptionSe lo desideri, puoi configurare regole di conversione delle eccezioni più specifiche (ad es. ). Se non sono disponibili altre regole, le eccezioni selezionate vengono convertite automaticamente in RuntimeException.

(Dichiarazione di non responsabilità: sono l'autore di ET)


2
Sembra che tu sia l'autore di questa libreria. Secondo le regole SO , devi rivelare la tua affiliazione nelle tue risposte. Aggiungi esplicitamente alla tua risposta che hai scritto questa libreria (lo stesso per altre risposte relative a ET).
Tagir Valeev,

2
Ciao Tagir, grazie per il suggerimento. Ho aggiornato la risposta.
Micha,

2

Quello che sto facendo è consentire all'utente di fornire il valore che desidera effettivamente in caso di eccezione. Quindi ho qualcosa di simile a questo

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 questo può quindi essere chiamato come:

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

1
Questa è un'estensione di questa idea che fa una distinzione tra una strategia di "valore predefinito" (come nella tua risposta) e una strategia di "ripensare RuntimeException", in cui un valore predefinito non è necessario.
Ned Twigg,

2

Se non ti dispiace usare una libreria di terze parti, con cyclops-reagire , una libreria a cui contribuisco, puoi usare l' API FluentFunctions per scrivere

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

ofChecked accetta una jOOλ CheckedFunction e restituisce il riferimento rammollito a una JDK standard (non selezionata) java.util.function.Function.

In alternativa puoi continuare a lavorare con la funzione acquisita tramite l'API FluentFunctions!

Ad esempio per eseguire il metodo, riprovandolo fino a 5 volte e registrando il suo stato è possibile scrivere

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

2

Per impostazione predefinita, la funzione Java 8 non consente di generare eccezioni e, come suggerito in più risposte, ci sono molti modi per raggiungerlo, un modo è:

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

Definisci come:

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

E aggiungi throwso try/catchla stessa eccezione nel metodo del chiamante.


2

Creare un tipo di ritorno personalizzato che propagherà l'eccezione selezionata. Questa è un'alternativa alla creazione di una nuova interfaccia che rispecchia l'interfaccia funzionale esistente con la leggera modifica di una "eccezione di lancio" sul metodo dell'interfaccia funzionale.

Definizione

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

Cosa sta succedendo?

Viene creata una singola interfaccia funzionale che genera un'eccezione selezionata ( CheckedValueSupplier). Questa sarà l'unica interfaccia funzionale che consente eccezioni verificate. Tutte le altre interfacce funzionali trarranno vantaggio dall'involucro CheckedValueSupplierdi qualsiasi codice che genera un'eccezione controllata.

La CheckedValueclasse conterrà il risultato dell'esecuzione di qualsiasi logica che genera un'eccezione controllata. Ciò impedisce la propagazione di un'eccezione controllata fino al punto in cui il codice tenta di accedere al valore contenuto da un'istanza CheckedValue.

I problemi con questo approccio.

  • Ora stiamo lanciando "Eccezione" per nascondere efficacemente il tipo specifico originariamente lanciato.
  • Non sappiamo che si CheckedValue#get()è verificata un'eccezione fino a quando non viene chiamata.

Consumer et al

Alcune interfacce funzionali (Consumer ad esempio) devono essere gestite in modo diverso poiché non forniscono un valore di ritorno.

Funzione al posto del consumatore

Un approccio consiste nell'utilizzare una funzione anziché un consumatore, che si applica durante la gestione dei flussi.

    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

escalate

In alternativa, puoi sempre passare a RuntimeException. Esistono altre risposte che coprono l'escalation di un'eccezione controllata all'interno di a Consumer.

Non consumare

Basta evitare le interfacce funzionali tutte insieme e utilizzare un ciclo ben fatto per il loop.


2

Uso una funzione di utilità sovraccarica chiamata unchecked()che gestisce più casi d'uso.


ALCUNI ESEMPI DI UTILIZZO

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

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

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

UTILITÀ DI SUPPORTO

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

0

Molte delle soluzioni offerte usano un argomento generico di E per passare il tipo di eccezione che viene generata.

Fai un ulteriore passo avanti e, anziché passare nel tipo di eccezione, passa un Consumatore del tipo di eccezione, come in ...

Consumer<E extends Exception>

È possibile creare diverse varianti riutilizzabili di Consumer<Exception>cui coprire le esigenze comuni di gestione delle eccezioni dell'applicazione.


0

Farò qualcosa di generico:

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

        }
    }
}

utilizzo:

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

0

Utilizzare Jool Libraryo dire jOOλ librarydaJOOQ . Non solo fornisce interfacce gestite per eccezioni non controllate, ma fornisce anche alla classe Seq molti metodi utili.

Inoltre, contiene interfacce funzionali con un massimo di 16 parametri. Inoltre, fornisce la classe Tuple che viene utilizzata in diversi scenari.

Jool Git Link

In particolare nella ricerca della libreria per il org.jooq.lambda.fi.util.functionpacchetto. Contiene tutte le interfacce da Java-8 con Checked anteposto. Vedi sotto per riferimento: -

inserisci qui la descrizione dell'immagine


0

Sono l'autore di una piccola lib con un po 'di magia generica per lanciare qualsiasi eccezione Java ovunque senza la necessità di catturarli o avvolgerliRuntimeException .

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


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

3
Ti dispiace commentare il tuo lavoro? Le risposte di solo codice non sono così utili.
Phantômaxx,

@Franky puoi correggere la tua presentazione usando 4 spazi, invece di <code>/<code>:)
AdrieanKhisbe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.