Fonction Java 8 Lambda qui lève une exception?

470

Je sais comment créer une référence à une méthode qui a un Stringparamètre et renvoie un int, c'est:

Function<String, Integer>

Cependant, cela ne fonctionne pas si la fonction lève une exception, disons qu'elle est définie comme:

Integer myMethod(String s) throws IOException

Comment définirais-je cette référence?

Triton Man
la source
1
Connexe: stackoverflow.com/questions/31637892/…
Marko Topolnik
1
... et celui-ci: stackoverflow.com/questions/31270759/…
Vadzim
4
Toute la solution ressemble à quelque chose, en jetant des exceptions d'exécution, je crois que ce n'est pas une bonne solution. donc il vaut mieux utiliser l'ancien java pour les boucles
Nazeel
5
Et la bibliothèque Jool ? cf org.jooq.lambda.Unchecked package
chaiyachaiya

Réponses:

403

Vous devrez effectuer l'une des opérations suivantes.

  • Si c'est votre code, définissez votre propre interface fonctionnelle qui déclare l'exception vérifiée:

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

    et l'utiliser:

    void foo (CheckedFunction f) { ... }
  • Sinon, encapsulez Integer myMethod(String s)dans une méthode qui ne déclare pas d'exception vérifiée:

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

    et alors:

    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
la source
7
Vous pouvez réellement étendre Consumerou Functionsi vous utilisez des méthodes par défaut - voir ma réponse ci-dessous.
jlb
2
Je pense que cela peut être accompli en une seule ligne .
Ned Twigg
6
Optimisation mineure: au lieu de (String t) -> myWrappedMethod(t), la référence de méthode this::myWrappedMethodpeut également être utilisée.
Clashsoft
8
Une façon encore plus générique de le faire est de définir la fonction vérifiée comme cette interface publique @FunctionalInterface CheckedFunction <T, R, E étend l'exception> {R applique (T t) lève E; } De cette façon, vous pouvez également définir quelle exception la fonction lance et pouvez réutiliser l'interface pour n'importe quel code.
Martin Odhelius
3
Sensationnel. Java est pire que ce que je pensais
user275801
194

Vous pouvez réellement étendre Consumer(et Functionetc.) avec une nouvelle interface qui gère les exceptions - en utilisant les méthodes par défaut de Java 8 !

Considérez cette interface (étend 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;

}

Ensuite, par exemple, si vous avez une liste:

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

Si vous voulez le consommer (par exemple avec forEach) avec du code qui lève des exceptions, vous auriez traditionnellement mis en place un bloc 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);

Mais avec cette nouvelle interface, vous pouvez l'instancier avec une expression lambda et le compilateur ne se plaindra pas:

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

Ou même simplement le lancer pour être plus succinct!:

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

Mise à jour : Il semble qu'il y ait une très belle partie de bibliothèque d'utilitaires de Durian appelée Erreurs qui peut être utilisée pour résoudre ce problème avec beaucoup plus de flexibilité. Par exemple, dans mon implémentation ci-dessus, j'ai explicitement défini la politique de gestion des erreurs ( System.out...ou throw RuntimeException), tandis que les erreurs de Durian vous permettent d'appliquer une politique à la volée via une large suite de méthodes utilitaires. Merci de le partager , @NedTwigg !.

Exemple d'utilisation:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));
jlb
la source
14
Vous disposez donc d'un ensemble d'interfaces (Function, Consumer, Supplier, ...) et d'un ensemble de politiques de gestion des erreurs (Throwing, System.out.println, ...). Je pense qu'il existe un moyen de faciliter l'utilisation de n'importe quelle politique avec n'importe quel type de fonction, sans avoir à copier-coller "ThrowingConsumer, ThrowingFunction, etc.".
Ned Twigg
1
quelque temps plus tard ... J'ai décidé d'utiliser des exceptions non vérifiées et de ne pas utiliser d'interfaces fonctionnelles supplémentaires ou de nouvelles bibliothèques -> la route facile, moins de frappe, une livraison plus rapide, n'est-ce pas.
aliopi
1
Voici une version améliorée utilisant un idiome de lancement sournois. Pas besoin de déballer RuntimeException dans CheckException.
myui
62

Je pense que la Errorsclasse de Durian combine bon nombre des avantages des diverses suggestions ci-dessus.

Pour inclure Durian dans votre projet, vous pouvez soit:

Ned Twigg
la source
Ou vous pouvez simplement utiliser RxJava car les flux ont besoin d'une gestion inhérente des erreurs et s'il y a quelque chose dans votre pipeline qui lève une exception, il y a de fortes chances que ce soit probablement un flux observable. Cela ne force pas non plus Java 8 sur les consommateurs en aval d'une bibliothèque.
Adam Gent
2
Veuillez noter que Durian n'a pas de nouvelles versions depuis juin 2016. Pas un bouchon de spectacle, mais quelque chose à garder à l'esprit.
Istvan Devai
9
Mainteneur Durian ici. Qu'est-ce qui est cassé? Si un utilisateur trouve un bug ou une fonctionnalité manquante importante, nous publierons rapidement un correctif. La bibliothèque est simple, donc nous n'avons pas eu de rapports de bogues, donc nous n'avons pas eu besoin de publier de corrections de bogues.
Ned Twigg
28

Ce n'est pas spécifique à Java 8. Vous essayez de compiler quelque chose d'équivalent à:

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}
assylias
la source
15
La question est "Comment définir cette référence?" . Cela ne répond pas réellement à la question; il clarifie simplement le problème.
Dawood ibn Kareem
13

Avis de non-responsabilité: je n'ai pas encore utilisé Java 8, lisez-le seulement.

Function<String, Integer>ne lance pas IOException, donc vous ne pouvez pas y mettre de code throws IOException. Si vous appelez une méthode qui attend a Function<String, Integer>, le lambda que vous passez à cette méthode ne peut pas lancer IOException, point. Vous pouvez soit écrire un lambda comme celui-ci (je pense que c'est la syntaxe lambda, pas sûr):

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

Ou, si la méthode à laquelle vous passez le lambda est celle que vous avez écrite vous-même, vous pouvez définir une nouvelle interface fonctionnelle et l'utiliser comme type de paramètre au lieu de Function<String, Integer>:

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}
Adam R. Nelson
la source
ajoutez l'annotation @FunctionalInterface avant votre interface, alors seulement elle sera utilisable pour les lambdas.
Gangnus
13
@Gangnus: l' @FunctionalInterfaceannotation n'est pas requise pour qu'elle soit utilisable pour les lambdas. Il est cependant recommandé de vérifier la santé mentale.
Tanmay Patil
9

Si cela ne vous dérange pas d'utiliser une bibliothèque tierce ( Vavr ), vous pouvez écrire

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

Il a également la soi-disant Try monad qui gère les erreurs:

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

Veuillez lire plus ici .

Avertissement: je suis le créateur de Vavr.

Daniel Dietrich
la source
7

Vous pouvez utiliser le décapsuleur

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

ou

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

Vous pouvez cependant créer votre propre FunctionalInterface qui lance comme ci-dessous.

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

puis implémentez-le en utilisant Lambdas ou des références comme indiqué ci-dessous.

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
la source
6

Vous pouvez.

Étendre @marcg UtilExceptionet ajouter des génériques <E extends Exception>si nécessaire: de cette façon, le compilateur vous forcera à nouveau à ajouter des clauses throw et tout comme si vous pouviez lancer des exceptions vérifiées nativement sur les flux de 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
la source
5

J'ai eu ce problème avec Class.forName et Class.newInstance dans un lambda, donc je viens de le faire:

public Object uncheckedNewInstanceForName (String name) {

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

À l'intérieur du lambda, au lieu d'appeler Class.forName ("myClass"). NewInstance () Je viens d'appeler uncheckedNewInstanceForName ("myClass")

Sergio
la source
4

Une autre solution utilisant un wrapper Function serait de renvoyer soit une instance d'un wrapper de votre résultat, disons Success, si tout s'est bien passé, soit une instance de, disons Failure.

Un code pour clarifier les choses:

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 cas d'utilisation simple:

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

Ce problème me dérange également; c'est pourquoi j'ai créé ce projet .

Avec lui, vous pouvez faire:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

Il y a un total de 39 interfaces définies par le JDK qui ont un tel Throwingéquivalent; ce sont tous des @FunctionalInterfaces utilisés dans les streams (la base Streammais aussi IntStream, LongStreamet DoubleStream).

Et comme chacun d'eux étend son homologue non lanceur, vous pouvez également les utiliser directement dans les lambdas:

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

Le comportement par défaut est que lorsque votre lancement lambda lève une exception vérifiée, un ThrownByLambdaExceptionest levé avec l'exception vérifiée comme cause. Vous pouvez donc capturer cela et obtenir la cause.

D'autres fonctionnalités sont également disponibles.

fge
la source
J'aime vraiment l'idée, je souhaite seulement que vous ayez rendu les jetables génériques comme suggéré ici: javaspecialists.eu/archive/Issue221.html , par exemple: @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }- de cette façon, l'utilisateur n'a pas besoin d'attraper Throwable, mais l'exception vérifiée spécifique à la place.
Zoltán
@ Zoltán, ce serait une peine de déclarer l'exception à chaque fois; De plus, vous pouvez toujours simplement utiliser, par exemple, .apply () au lieu de .doApply () et attraper ThrownByLambdaException, vous aurez l'exception d'origine comme cause (ou vous pouvez utiliser rethrow(...).as(MyRuntimeException.class))
fge
Je pense qu'il y a (en quelque sorte) un moyen de contourner cela .
Ned Twigg
@NedTwigg J'ai également résolu ce problème il y a longtemps; Je peux maintenant utiliser Throwing.runnable()et d'autres, toujours avec des capacités de chaînage
fge
La fonctionnalité de chaînage est très cool! Mon commentaire était de savoir si ThrowingRunnable devrait avoir l'exception générique ou non. Zoltan a demandé si votre bibliothèque pouvait avoir l'argument comme paramètre générique, et vous avez dit que ce serait pénible à utiliser. Mon lien était vers certaines lignes de code qui montrent une façon de faire en sorte que les exceptions soient génériques, sans que cela soit pénible. Sauf si je l'ai mal lu, les exceptions dans votre bibliothèque ne sont pas génériques (ce qui est un choix de conception raisonnable, car vous ne recevez pas beaucoup d'utilité en les rendant génériques).
Ned Twigg
4

Il y a beaucoup de bonnes réponses déjà publiées ici. J'essaie juste de résoudre le problème avec une perspective différente. C'est juste mes 2 cents, veuillez me corriger si je me trompe quelque part.

La clause throws dans FunctionalInterface n'est pas une bonne idée

Je pense que ce n'est probablement pas une bonne idée d'appliquer les exceptions IOException pour les raisons suivantes

  • Cela me semble être un anti-pattern pour Stream / Lambda. L'idée est que l'appelant décidera du code à fournir et comment gérer l'exception. Dans de nombreux scénarios, l'exception IOException peut ne pas être applicable au client. Par exemple, si le client obtient de la valeur du cache / de la mémoire au lieu d'effectuer des E / S réelles.

  • De plus, la gestion des exceptions dans les flux devient vraiment hideuse. Par exemple, voici mon code ressemblera si j'utilise votre 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;
                });

    Moche n'est-ce pas? De plus, comme je l'ai mentionné dans mon premier point, que la méthode doSomeOperation peut ou non lancer IOException (selon l'implémentation du client / appelant), mais en raison de la clause throws de votre méthode FunctionalInterface, je dois toujours écrire le essayer-attraper.

Que dois-je faire si je sais vraiment que cette API lève IOException

  • Ensuite, nous confondons probablement FunctionalInterface avec des interfaces typiques. Si vous savez que cette API lèvera IOException, alors vous connaissez probablement aussi un comportement par défaut / abstrait. Je pense que vous devez définir une interface et déployer votre bibliothèque (avec une implémentation par défaut / abstraite) comme suit

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

    Mais, le problème try-catch existe toujours pour le client. Si j'utilise votre API dans le flux, j'ai toujours besoin de gérer IOException dans un bloc try-catch hideux.

  • Fournissez une API conviviale par défaut comme suit

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

    La méthode par défaut prend l'objet consommateur comme argument, qui sera chargé de gérer l'exception. Maintenant, du point de vue du client, le code ressemblera à ceci

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

    Bonne droite? Bien sûr, un enregistreur ou une autre logique de gestion peut être utilisé à la place d'Exception :: printStackTrace.

  • Vous pouvez également exposer une méthode similaire à https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- . Cela signifie que vous pouvez exposer une autre méthode, qui contiendra l'exception de l'appel de méthode précédent. L'inconvénient est que vous rendez maintenant vos API statiques, ce qui signifie que vous devez gérer la sécurité des threads et que cela finira par devenir un problème de performances. Mais juste une option à considérer.

TriCore
la source
Je conviens que la conversion de l'exception vérifiée en une exception non vérifiée ou l'ingestion de l'exception n'est pas une bonne idée car il n'y a aucun moyen de savoir quel élément de l' Streamexception déclenchée. Ainsi, j'aime l'idée d'avoir un gestionnaire d'exceptions et de filtrer les résultats qui ne sont pas valides. Notez que votre MyAmazingAPI est effectivement un FunctionalInterface(vous pouvez donc ajouter l'annotation @FunctionalInterface). Vous pouvez également avoir une valeur par défaut au lieu d'utiliser Optional.empty().
Julien Kronegg
4

L'idiome de projection sournoise permet de contourner l' CheckedExceptionexpression Lambda. Envelopper unCheckedException dans un RuntimeExceptionn'est pas bon pour une gestion stricte des erreurs.

Il peut être utilisé comme Consumer fonction utilisée dans une collection Java.

Voici une version simple et améliorée de la réponse de 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);
        }
    }));
}

Cela enveloppe simplement le lambda dans un nouveau jet . Il fait CheckedExceptionjeter tout ce Exceptionqui a été jeté dans votre 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;
    }

}

Trouvez un code complet et des tests unitaires ici .

myui
la source
3

Vous pouvez utiliser ET pour cela. ET est une petite bibliothèque Java 8 pour la conversion / traduction d'exceptions.

Avec ET, cela ressemble à ceci:

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

ExceptionTranslatorles instances sont thread-safe et peuvent être partagées par plusieurs composants. Vous pouvez configurer des règles de conversion d'exception plus spécifiques (par exemple FooCheckedException -> BarRuntimeException) si vous le souhaitez. Si aucune autre règle n'est disponible, les exceptions vérifiées sont automatiquement converties en RuntimeException.

(Avertissement: je suis l'auteur de ET)

micha
la source
2
On dirait que vous êtes l'auteur de cette bibliothèque. Selon les règles des SO , vous devez divulguer votre affiliation dans vos réponses. Veuillez ajouter explicitement à votre réponse que vous avez écrit cette bibliothèque (la même chose pour les autres réponses liées à ET).
Tagir Valeev
2
Salut Tagir, merci pour l'astuce. J'ai mis à jour la réponse.
micha
2

Ce que je fais, c'est de permettre à l'utilisateur de donner la valeur qu'il souhaite réellement en cas d'exception. J'ai donc quelque chose qui ressemble à ça

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

Et cela peut alors être appelé comme:

defaultIfThrows(child -> child.getID(), null)
mmounirou
la source
1
Il s'agit d'une extension de cette idée qui fait une distinction entre une stratégie de "valeur par défaut" (comme dans votre réponse), et une stratégie de "relance RuntimeException", où une valeur par défaut n'est pas nécessaire.
Ned Twigg
2

Si cela ne vous dérange pas d'utiliser une bibliothèque tierce, avec cyclops-react , une bibliothèque à laquelle je contribue, vous pouvez utiliser l' API FluentFunctions pour écrire

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

ofChecked prend une fonction jOOλ CheckedFunction et renvoie la référence ramollie à une fonction JDK java.util.function.Function standard (non vérifiée).

Alternativement, vous pouvez continuer à travailler avec la fonction capturée via l'API FluentFunctions!

Par exemple, pour exécuter votre méthode, en la réessayant jusqu'à 5 fois et en enregistrant son état, vous pouvez l'écrire

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

Par défaut, la fonction Java 8 ne permet pas de lever d'exception et, comme suggéré dans plusieurs réponses, il existe de nombreuses façons de le réaliser, dont l'une est:

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

Définir en tant que:

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

Et ajoutez throwsou try/catchla même exception dans la méthode de l'appelant.

Arpit Aggarwal
la source
2

Créez un type de retour personnalisé qui propagera l'exception vérifiée. Il s'agit d'une alternative à la création d'une nouvelle interface qui reflète l'interface fonctionnelle existante avec la légère modification d'une "exception de levée" sur la méthode de l'interface fonctionnelle.

Définition

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

}

Usage

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

Que se passe-t-il?

Une interface fonctionnelle unique qui lève une exception vérifiée est créée ( CheckedValueSupplier). Ce sera la seule interface fonctionnelle qui autorise les exceptions vérifiées. Toutes les autres interfaces fonctionnelles tireront parti de CheckedValueSupplierpour envelopper tout code qui lève une exception vérifiée.

La CheckedValueclasse contiendra le résultat de l'exécution de toute logique qui lève une exception vérifiée. Cela empêche la propagation d'une exception vérifiée jusqu'au moment où le code tente d'accéder à la valeur contenue dans une instance CheckedValue.

Les problèmes avec cette approche.

  • Nous lançons maintenant "Exception" en masquant efficacement le type spécifique levé à l'origine.
  • Nous ne savons pas qu'une exception s'est produite jusqu'à CheckedValue#get() soit appelée.

Consumer et al

Quelques interfaces fonctionnelles (Consumer par exemple) doivent être traitées de manière différente car elles ne fournissent pas de valeur de retour.

Fonction à la place du consommateur

Une approche consiste à utiliser une fonction au lieu d'un consommateur, qui s'applique lors de la gestion des flux.

    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

Intensifier

Alternativement, vous pouvez toujours passer à a RuntimeException. Il existe d'autres réponses qui couvrent l'escalade d'une exception vérifiée de l'intérieur a Consumer.

Ne consommez pas.

Évitez simplement les interfaces fonctionnelles toutes ensemble et utilisez une boucle for-old-fashioned for.

justin.hughey
la source
2

J'utilise une fonction utilitaire surchargée appelée unchecked()qui gère plusieurs cas d'utilisation.


QUELQUES UTILISATIONS PAR EXEMPLE

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

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

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

SOUTIEN AUX UTILITAIRES

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
la source
0

Plusieurs des solutions proposées utilisent un argument générique de E pour passer le type de l'exception qui est levée.

Allez plus loin, et plutôt que de passer le type d'exception, passez un consommateur du type d'exception, comme dans ...

Consumer<E extends Exception>

Vous pouvez créer plusieurs variantes réutilisables Consumer<Exception>qui couvriraient les besoins courants de gestion des exceptions de votre application.

Rodney P. Barbati
la source
0

Je vais faire quelque chose de générique:

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

        }
    }
}

usage:

 Lambda.handle(() -> method());
ahll
la source
0

Utilisez Jool Libraryou dites jOOλ libraryde JOOQ. Il fournit non seulement des interfaces gérées d'exception non contrôlées, mais fournit également à la classe Seq de nombreuses méthodes utiles.

En outre, il contient des interfaces fonctionnelles avec jusqu'à 16 paramètres. En outre, il fournit la classe Tuple qui est utilisée dans différents scénarios.

Lien Jool Git

Plus précisément dans la recherche de bibliothèque pour le org.jooq.lambda.fi.util.functionpackage. Il contient toutes les interfaces de Java-8 avec Checked ajouté. Voir ci-dessous pour référence: -

entrez la description de l'image ici

Vinay Prajapati
la source
0

Je suis l'auteur d'une petite bibliothèque avec une magie générique pour lancer n'importe quelle exception Java n'importe où sans avoir besoin de les attraper ni de les envelopperRuntimeException .

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

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

qoomon
la source
-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
la source
3
Voulez-vous commenter votre travail? Les réponses uniquement codées ne sont pas si utiles.
Phantômaxx
@Franky, vous pouvez corriger votre présentation en utilisant un espacement de 4, au lieu de <code>/<code>:)
AdrieanKhisbe