web-dev-qa-db-fra.com

Pourquoi ne puis-je pas lancer une exception dans une expression lambda Java 8?)?

J'ai mis à niveau vers Java 8 et j'ai essayé de remplacer une simple itération par une carte avec une nouvelle expression lamdba. La boucle recherche les valeurs nulles et lève une exception, le cas échéant. L'ancienne Java 7 code ressemble à ceci:

for (Map.Entry<String, String> entry : myMap.entrySet()) {
    if(entry.getValue() == null) {
        throw new MyException("Key '" + entry.getKey() + "' not found!");
    }
}

Et ma tentative de convertir ceci en Java 8 ressemble à ceci:

myMap.forEach((k,v) -> {
    if(v == null) {
        // OK
        System.out.println("Key '" + k+ "' not found!");

        // NOK! Unhandled exception type!
        throw new MyException("Key '" + k + "' not found!");
    }
});

Quelqu'un peut-il expliquer pourquoi l'instruction throw n'est pas autorisée ici et comment cela pourrait être corrigé?

La suggestion de solution rapide d'Eclipse ne me semble pas correcte… elle entoure simplement l'instruction throw d'un caractère try-catch bloc:

myMap.forEach((k,v) -> {
    if(v == null) {
        try {
            throw new MyException("Key '" + k + "' not found!");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
});
29
dokaspar

Vous n'êtes pas autorisé à lancer des exceptions vérifiées car la méthode accept(T t, U u) dans le Java.util.function.BiConsumer<T, U> l’interface ne déclare aucune exception dans sa clause throws. Et, comme vous le savez, Map#forEach prend un tel type.

public interface Map<K, V> {

    default void forEach(BiConsumer<? super K, ? super V> action) { ... }

}                        |
                         |
                         V
@FunctionalInterface
public interface BiConsumer<T, U> {

    void accept(T t, U u); // <-- does throw nothing

}

C'est vrai quand on parle d'exceptions vérifiées. Mais vous pouvez toujours lever une exception non contrôlée (par exemple, un Java.lang.IllegalArgumentException ):

new HashMap<String, String>()
    .forEach((a, b) -> { throw new IllegalArgumentException(); });
45
Andrew Tobilko

Vous pouvez lancer des exceptions dans lambdas.

Un lambda est autorisé à lancer les mêmes exceptions que l'interface fonctionnelle implémentée par le lambda.

Si la méthode de l'interface fonctionnelle ne comporte pas de clause throws, le lambda ne peut pas lancer CheckedExceptions. (Vous pouvez toujours lancer RuntimeExceptions).


Dans votre cas particulier, Map.forEach utilise un paramètre BiConsumer, BiConsumer est défini comme suit:

public interface BiConsumer<T, U> {
    void accept(T t, U u);
}

Une expression lambda pour cette interface fonctionnelle ne peut pas lancer CheckedExceptions.


Les méthodes dans les interfaces fonctionnelles définies dans Java.util.function package ne génère pas d’exception, mais vous pouvez utiliser d’autres interfaces fonctionnelles ou créer la vôtre pour pouvoir générer des exceptions, c’est-à-dire avec cette interface:

public interface MyFunctionalInterface<T> {
    void accept(T t) throws Exception;
}

Le code suivant serait légal:

MyFunctionalInterface<String> f = (s)->throw new Exception("Exception thrown in lambda"); 
25
David SN