web-dev-qa-db-fra.com

Java8 Lambdas et Exceptions

Je me demande si quelqu'un pourrait m'expliquer la bizarrerie suivante. J'utilise Java 8 update 11.

Étant donné cette méthode

private <F,T> T runFun(Function<Optional<F>, T> fun, Optional<F> opt) {
   return fun.apply(opt) ;
}

Si je construis d'abord une fonction Object et que je la transmets à la méthode ci-dessus, les choses se compilent.

private void doesCompile() {
    Function<Optional<String>, String> fun = o -> o.orElseThrow(() -> new RuntimeException("nah"));
    runFun(fun, Optional.of("foo"));

}

Mais, si j'intègre la fonction en tant que lambda, le compilateur dit 

exception X non déclarée; doit être attrapé ou déclaré comme jeté

private void doesNotCompile () {
    runFun(o -> o.orElseThrow(() -> new RuntimeException("nah")), Optional.of("foo"));
}

Update: Il s'avère que le message d'erreur a été abrégé par maven. Si compilé directement avec javac, l'erreur est la suivante:

error: unreported exception X; must be caught or declared to be thrown
            runFun(o -> o.orElseThrow(() -> new RuntimeException("nah")), Optional.of("foo"));
                                     ^
  where X,T are type-variables:
    X extends Throwable declared in method <X>orElseThrow(Supplier<? extends X>)
    T extends Object declared in class Optional

Voir aussi ici pour le code de test exécutable.

60
rompetroll

Cela ressemble à un cas de bogue JDK-8054569 , qui n'affecte pas Eclipse.

J'ai pu le réduire en remplaçant Function par Supplier et en extrayant la méthode orElseThrow:

abstract <T> void f(Supplier<T> s);

abstract <T, X extends Throwable> T g(Supplier<? extends X> x) throws X;

void bug() {
    f(() -> g(() -> new RuntimeException("foo")));
}

et ensuite en supprimant les fournisseurs et les lambdas:

abstract <T> void f(T t);

abstract <T, X extends Throwable> T g(X x) throws X;

void bug() {
    f(g(new RuntimeException("foo")));
}

ce qui est en fait un exemple plus propre que celui du rapport de bogue. Cela montre la même erreur si compilé en Java 8, mais fonctionne bien avec -source 1.7.

J'imagine que le fait de passer un type de retour de méthode générique à un paramètre de méthode générique provoque l'échec de l'inférence de type pour l'exception. Il suppose donc que le type est Throwable et se plaint que ce type d'exception cochée n'est pas géré. L'erreur disparaît si vous déclarez bug() throws Throwable ou si vous modifiez la liaison à X extends RuntimeException (elle est donc décochée).

36
Sean Van Gorder

C'est ce qui a résolu le problème pour moi:

au lieu d'écrire

optional.map(this::mappingFunction).orElseThrow(() -> new BadRequestException("bla bla"));

J'ai écrit:

optional.map(this::mappingFunction).<BadRequestException>orElseThrow(() -> new BadRequestException("bla bla"));

L'ajout du <BadRequestException> explicite aide ces cas Edge lambda (qui sont assez ennuyeux ...)

UPDATE: C'est au cas où vous ne pourriez pas mettre à jour la dernière version du JDK, si vous le pouvez vous devriez.

72
keisar

Si vous essayez de compiler le projet de quelqu'un d'autre, essayez de passer à la version 1.8.0_92.

2
Igor Katkov

Semblable à @keisar, je pourrais résoudre mon problème (voir maven-compiler-plugin ne parvient pas à compiler un fichier avec lequel Eclipse n'a pas de problème avec ) en spécifiant le paramètre type.

Cependant, j’ai trouvé beaucoup plus pratique (puisque j’utilise NotFoundException dans un certain nombre d’endroits) de simplement faire en sorte que ma classe d’exception soit sa propre Supplier:

public class NotFoundException extends RuntimeException
    implements Supplier<NotFoundException> {

    // Rest of the code

    @Override
    public NotFoundException get() {
        return this;
    }

}

Ensuite, vous pouvez simplement faire:

// Distribution.rep().get(id) returns a Java.util.Optional
Distribution distro = Distribution.rep().get(id).orElseThrow(
    new NotUniqueException("Exception message"));
0
markvgti