Java 8: Lambda-Streams, filtrer par méthode avec exception

168

J'ai un problème pour essayer les expressions Lambda de Java 8. Habituellement, cela fonctionne bien, mais maintenant j'ai des méthodes qui lancent IOExceptiondes. Il est préférable de consulter le code suivant:

class Bank{
    ....
    public Set<String> getActiveAccountNumbers() throws IOException {
        Stream<Account> s =  accounts.values().stream();
        s = s.filter(a -> a.isActive());
        Stream<String> ss = s.map(a -> a.getNumber());
        return ss.collect(Collectors.toSet());
    }
    ....
}

interface Account{
    ....
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
    ....
}

Le problème est qu'il ne compile pas, car je dois attraper les exceptions possibles des méthodes isActive et getNumber. Mais même si j'utilise explicitement un try-catch-Block comme ci-dessous, il ne se compile toujours pas car je n'attrape pas l'exception. Donc, soit il y a un bogue dans JDK, soit je ne sais pas comment attraper ces exceptions.

class Bank{
    ....
    //Doesn't compile either
    public Set<String> getActiveAccountNumbers() throws IOException {
        try{
            Stream<Account> s =  accounts.values().stream();
            s = s.filter(a -> a.isActive());
            Stream<String> ss = s.map(a -> a.getNumber());
            return ss.collect(Collectors.toSet());
        }catch(IOException ex){
        }
    }
    ....
}

Comment puis-je le faire fonctionner? Quelqu'un peut-il me suggérer la bonne solution?

Martin Weber
la source
1
En relation: stackoverflow.com/questions/31637892/…
Marko Topolnik
4
La réponse simple et correcte: attraper l'exception à l'intérieur du lambda.
Brian Goetz

Réponses:

211

Vous devez intercepter l'exception avant qu'elle n'échappe au lambda:

s = s.filter(a -> { try { return a.isActive(); } 
                    catch (IOException e) { throw new UncheckedIOException(e); }}});

Considérez le fait que le lambda n'est pas évalué à l'endroit où vous l'écrivez, mais à un endroit totalement indépendant, dans une classe JDK. Ce serait donc le moment où cette exception vérifiée serait lancée, et à cet endroit, elle n'est pas déclarée.

Vous pouvez le gérer en utilisant un wrapper de votre lambda qui traduit les exceptions vérifiées en exceptions non vérifiées:

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (RuntimeException e) { throw e; }
  catch (Exception e) { throw new RuntimeException(e); }
}

Votre exemple serait écrit comme

return s.filter(a -> uncheckCall(a::isActive))
        .map(Account::getNumber)
        .collect(toSet());

Dans mes projets, je traite ce problème sans emballage; à la place, j'utilise une méthode qui désactive efficacement la vérification des exceptions par le compilateur. Inutile de dire que cela doit être traité avec précaution et que tout le monde sur le projet doit être conscient qu'une exception vérifiée peut apparaître là où elle n'est pas déclarée. Voici le code de plomberie:

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (Exception e) { return sneakyThrow(e); }
}
public static void uncheckRun(RunnableExc r) {
  try { r.run(); } catch (Exception e) { sneakyThrow(e); }
}
public interface RunnableExc { void run() throws Exception; }


@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
  throw (T) t;
}

et vous pouvez vous attendre à être IOExceptionjeté au visage, même si vous collectne le déclarez pas. Dans la plupart des cas, mais pas dans tous les cas réels, vous voudrez simplement renvoyer l'exception, de toute façon, et la traiter comme un échec générique. Dans tous ces cas, rien n'est perdu dans la clarté ou l'exactitude. Méfiez-vous simplement de ces autres cas, où vous voudriez réellement réagir à l'exception sur place. Le développeur ne sera pas informé par le compilateur qu'il y a un IOExceptionà attraper là-bas et le compilateur se plaindra en fait si vous essayez de l'attraper parce que nous l'avons trompé en lui faisant croire qu'aucune exception de ce genre ne peut être levée.

Marko Topolnik
la source
4
J'ai déjà vu NettyIO faire le "lancer sournois" et je voulais jeter ma chaise par la fenêtre. "Quoi? D'où vient cette exception vérifiée?" C'est le premier cas d'utilisation légitime de lancer sournois que j'ai encore vu. En tant que programmeur, vous devez être vigilant pour indiquer que le lancer sournois est possible. Peut-être que mieux est simplement de créer une autre interface de flux / impl qui prend en charge les exceptions vérifiées?
kevinarpe
8
Aucune API sensée ne doit fournir des exceptions vérifiées non déclarées au client, c'est certain. Dans l'API, il peut y avoir une compréhension, cependant, que l'exception vérifiée peut fuir. Ils ne causent pas de dommages tant qu'ils ne sont qu'un autre signe d'échec général et qu'ils sont sur le point d'être pris et manipulés en gros.
Marko Topolnik
5
@kevinarpe C'est la raison exacte pour laquelle les lancers sournois sont une mauvaise idée. Le court-circuitage du compilateur risque de semer la confusion chez les futurs responsables.
Thorbjørn Ravn Andersen
29
Ce n'est pas parce que vous n'aimez pas les règles que c'est une bonne idée de prendre la loi en main. Votre conseil est irresponsable car il place la commodité de l'écrivain de code sur les considérations bien plus importantes de transparence et de maintenabilité du programme.
Brian Goetz
34
@brian Ce n'est pas parce que quelque chose est une règle que c'est une bonne idée. Mais je suis surpris que vous vous référiez à la deuxième partie de ma réponse en tant que «conseil», car je pensais avoir clairement mis en évidence ce que je propose comme solution, et ce que j'offre en tant que FYI au lecteur intéressé, avec de nombreuses clauses de non-responsabilité.
Marko Topolnik
29

Vous pouvez également propager votre douleur statique avec des lambdas, de sorte que tout semble lisible:

s.filter(a -> propagate(a::isActive))

propagateici reçoit java.util.concurrent.Callableen paramètre et convertit toute exception interceptée lors de l'appel en RuntimeException. Il existe une méthode de conversion similaire Throwables # propagate (Throwable) dans Guava.

Cette méthode semble être essentielle pour le chaînage de méthodes lambda, donc j'espère qu'un jour elle sera ajoutée à l'une des bibliothèques populaires ou que ce comportement de propagation le serait par défaut.

public class PropagateExceptionsSample {
    // a simplified version of Throwables#propagate
    public static RuntimeException runtime(Throwable e) {
        if (e instanceof RuntimeException) {
            return (RuntimeException)e;
        }

        return new RuntimeException(e);
    }

    // this is a new one, n/a in public libs
    // Callable just suits as a functional interface in JDK throwing Exception 
    public static <V> V propagate(Callable<V> callable){
        try {
            return callable.call();
        } catch (Exception e) {
            throw runtime(e);
        }
    }

    public static void main(String[] args) {
        class Account{
            String name;    
            Account(String name) { this.name = name;}

            public boolean isActive() throws IOException {
                return name.startsWith("a");
            }
        }


        List<Account> accounts = new ArrayList<>(Arrays.asList(new Account("andrey"), new Account("angela"), new Account("pamela")));

        Stream<Account> s = accounts.stream();

        s
          .filter(a -> propagate(a::isActive))
          .map(a -> a.name)
          .forEach(System.out::println);
    }
}
Andrey Chaschev
la source
22

Cette UtilExceptionclasse d'assistance vous permet d'utiliser toutes les exceptions vérifiées dans les flux Java, comme ceci:

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
      .map(rethrowFunction(Class::forName))
      .collect(Collectors.toList());

Remarque Class::forNamejette ClassNotFoundException, qui est vérifié . Le flux lui-même lève également ClassNotFoundException, et PAS une exception non cochée d'encapsulation.

public final class UtilException {

@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
    void accept(T t) throws E;
    }

@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
    void accept(T t, U u) throws E;
    }

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

@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
    T get() throws E;
    }

@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
    void run() throws E;
    }

/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
    return t -> {
        try { consumer.accept(t); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
    return (t, u) -> {
        try { biConsumer.accept(t, u); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

/** .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) { throwAsUnchecked(exception); return null; }
        };
    }

/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
    return () -> {
        try { return function.get(); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
    {
    try { t.run(); }
    catch (Exception exception) { throwAsUnchecked(exception); }
    }

/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
    {
    try { return supplier.get(); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
    try { return function.apply(t); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

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

}

De nombreux autres exemples d'utilisation (après importation statique UtilException):

@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(System.out::println));
    }

@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
    List<Class> classes1
          = Stream.of("Object", "Integer", "String")
                  .map(rethrowFunction(className -> Class.forName("java.lang." + className)))
                  .collect(Collectors.toList());

    List<Class> classes2
          = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
                  .map(rethrowFunction(Class::forName))
                  .collect(Collectors.toList());
    }

@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
    Collector.of(
          rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
          StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
    }

@Test    
public void test_uncheck_exception_thrown_by_method() {
    Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));

    Class clazz2 = uncheck(Class::forName, "java.lang.String");
    }

@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
    Class clazz3 = uncheck(Class::forName, "INVALID");
    }

Mais ne l'utilisez pas avant de comprendre les avantages, inconvénients et limitations suivants :

• Si le code d'appel doit gérer l'exception vérifiée, vous DEVEZ l'ajouter à la clause throws de la méthode qui contient le flux. Le compilateur ne vous obligera plus à l'ajouter, il est donc plus facile de l'oublier.

• Si le code appelant gère déjà l'exception vérifiée, le compilateur vous rappellera d'ajouter la clause throws à la déclaration de méthode qui contient le flux (si vous ne le faites pas, il dira: l'exception n'est jamais levée dans le corps de l'instruction try correspondante ).

• Dans tous les cas, vous ne pourrez pas entourer le flux lui-même pour attraper l'exception vérifiée À L'INTÉRIEUR de la méthode qui contient le flux (si vous essayez, le compilateur dira: l'exception n'est jamais levée dans le corps de l'instruction try correspondante).

• Si vous appelez une méthode qui ne peut littéralement jamais lever l'exception qu'elle déclare, vous ne devez pas inclure la clause throws. Par exemple: new String (byteArr, "UTF-8") lève une exception UnsupportedEncodingException, mais UTF-8 est garanti par la spécification Java pour être toujours présent. Ici, la déclaration des lancers est une nuisance et toute solution pour la faire taire avec un passe-partout minimal est la bienvenue.

• Si vous détestez les exceptions vérifiées et pensez qu'elles ne devraient jamais être ajoutées au langage Java pour commencer (un nombre croissant de personnes pensent de cette façon, et je ne suis PAS l'un d'entre eux), alors n'ajoutez simplement pas l'exception vérifiée au throws clause de la méthode qui contient le flux. L'exception vérifiée se comportera alors comme une exception non vérifiée.

• Si vous implémentez une interface stricte dans laquelle vous n'avez pas la possibilité d'ajouter une déclaration throws, mais que la levée d'une exception est tout à fait appropriée, alors encapsuler une exception juste pour obtenir le privilège de la lancer entraîne un stacktrace avec des exceptions fausses qui ne fournissent aucune information sur ce qui a réellement mal tourné. Un bon exemple est Runnable.run (), qui ne lève aucune exception vérifiée. Dans ce cas, vous pouvez décider de ne pas ajouter l'exception vérifiée à la clause throws de la méthode qui contient le flux.

• Dans tous les cas, si vous décidez de NE PAS ajouter (ou oubliez d'ajouter) l'exception vérifiée à la clause throws de la méthode qui contient le flux, soyez conscient de ces 2 conséquences de lever des exceptions CHECKED:

1) Le code appelant ne pourra pas l'attraper par son nom (si vous essayez, le compilateur dira: l'exception n'est jamais levée dans le corps de l'instruction try correspondante). Il bouillonnera et sera probablement attrapé dans la boucle principale du programme par des "catch Exception" ou "catch Throwable", ce qui peut être ce que vous voulez de toute façon.

2) Cela viole le principe de moindre surprise: il ne suffira plus d'attraper RuntimeException pour pouvoir garantir la capture de toutes les exceptions possibles. Pour cette raison, je pense que cela ne devrait pas être fait dans le code-cadre, mais uniquement dans le code métier que vous contrôlez complètement.

En conclusion: je pense que les limites ici ne sont pas sérieuses et que la UtilExceptionclasse peut être utilisée sans crainte. Cependant, c'est à vous!

MarcG
la source
8

Vous pouvez potentiellement rouler votre propre Streamvariante en enveloppant votre lambda pour lever une exception non vérifiée, puis en déroulant plus tard cette exception non vérifiée sur les opérations du terminal:

@FunctionalInterface
public interface ThrowingPredicate<T, X extends Throwable> {
    public boolean test(T t) throws X;
}

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

@FunctionalInterface
public interface ThrowingSupplier<R, X extends Throwable> {
    public R get() throws X;
}

public interface ThrowingStream<T, X extends Throwable> {
    public ThrowingStream<T, X> filter(
            ThrowingPredicate<? super T, ? extends X> predicate);

    public <R> ThrowingStream<T, R> map(
            ThrowingFunction<? super T, ? extends R, ? extends X> mapper);

    public <A, R> R collect(Collector<? super T, A, R> collector) throws X;

    // etc
}

class StreamAdapter<T, X extends Throwable> implements ThrowingStream<T, X> {
    private static class AdapterException extends RuntimeException {
        public AdapterException(Throwable cause) {
            super(cause);
        }
    }

    private final Stream<T> delegate;
    private final Class<X> x;

    StreamAdapter(Stream<T> delegate, Class<X> x) {
        this.delegate = delegate;
        this.x = x;
    }

    private <R> R maskException(ThrowingSupplier<R, X> method) {
        try {
            return method.get();
        } catch (Throwable t) {
            if (x.isInstance(t)) {
                throw new AdapterException(t);
            } else {
                throw t;
            }
        }
    }

    @Override
    public ThrowingStream<T, X> filter(ThrowingPredicate<T, X> predicate) {
        return new StreamAdapter<>(
                delegate.filter(t -> maskException(() -> predicate.test(t))), x);
    }

    @Override
    public <R> ThrowingStream<R, X> map(ThrowingFunction<T, R, X> mapper) {
        return new StreamAdapter<>(
                delegate.map(t -> maskException(() -> mapper.apply(t))), x);
    }

    private <R> R unmaskException(Supplier<R> method) throws X {
        try {
            return method.get();
        } catch (AdapterException e) {
            throw x.cast(e.getCause());
        }
    }

    @Override
    public <A, R> R collect(Collector<T, A, R> collector) throws X {
        return unmaskException(() -> delegate.collect(collector));
    }
}

Ensuite, vous pouvez l'utiliser exactement de la même manière que Stream:

Stream<Account> s = accounts.values().stream();
ThrowingStream<Account, IOException> ts = new StreamAdapter<>(s, IOException.class);
return ts.filter(Account::isActive).map(Account::getNumber).collect(toSet());

Cette solution nécessiterait un peu de passe-partout, donc je vous suggère de jeter un œil à la bibliothèque que j'ai déjà créée qui fait exactement ce que j'ai décrit ici pour toute la Streamclasse (et plus encore!).

Jeffrey
la source
Bonjour ... petit bug? new StreamBridge<>(ts, IOException.class);->new StreamBridge<>(s, IOException.class);
kevinarpe
1
@kevinarpe Ouais. Il aurait également dû le dire StreamAdapter.
Jeffrey
5

Utilisez la méthode #propagate (). Exemple d'implémentation non-Guava du blog Java 8 par Sam Beran :

public class Throwables {
    public interface ExceptionWrapper<E> {
        E wrap(Exception e);
    }

    public static <T> T propagate(Callable<T> callable) throws RuntimeException {
        return propagate(callable, RuntimeException::new);
    }

    public static <T, E extends Throwable> T propagate(Callable<T> callable, ExceptionWrapper<E> wrapper) throws E {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw wrapper.wrap(e);
        }
    }
}
n0mer
la source
Le lien du blog Java 8 est mort.
Spycho
4

Cela ne répond pas directement à la question (il y a beaucoup d'autres réponses qui le font) mais essaie d'éviter le problème en premier lieu:

D'après mon expérience, la nécessité de gérer les exceptions dans une Stream(ou une autre expression lambda) vient souvent du fait que les exceptions sont déclarées comme étant levées à partir de méthodes où elles ne devraient pas être levées. Cela vient souvent du mélange de logique métier avec entrée et sortie. Votre Accountinterface en est un parfait exemple:

interface Account {
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
}

Au lieu de lancer un IOExceptionsur chaque getter, considérez cette conception:

interface AccountReader {
    Account readAccount(…) throws IOException;
}

interface Account {
    boolean isActive();
    String getNumber();
}

La méthode AccountReader.readAccount(…)peut lire un compte à partir d'une base de données ou d'un fichier ou autre et lever une exception si elle échoue. Il construit un Accountobjet qui contient déjà toutes les valeurs, prêt à être utilisé. Comme les valeurs ont déjà été chargées par readAccount(…), les getters ne lèveraient pas d'exception. Ainsi, vous pouvez les utiliser librement dans les lambdas sans avoir besoin d'encapsuler, de masquer ou de masquer les exceptions.

Bien sûr, il n'est pas toujours possible de le faire comme je l'ai décrit, mais c'est souvent le cas et cela conduit à un code plus propre (IMHO):

  • Meilleure séparation des préoccupations et respect du principe de responsabilité unique
  • Moins de passe-partout: vous n'avez pas à encombrer votre code throws IOExceptionpour rien d'autre que pour satisfaire le compilateur
  • Gestion des erreurs: vous gérez les erreurs là où elles se produisent - lors de la lecture à partir d'un fichier ou d'une base de données - plutôt que quelque part au milieu de votre logique métier uniquement parce que vous souhaitez obtenir une valeur de champs
  • Vous pourrez peut-être rendre Account immuable et profiter de ses avantages (par exemple, la sécurité des fils)
  • Vous n'avez pas besoin de "sales trucs" ou de solutions de contournement à utiliser Accountdans les lambdas (par exemple dans a Stream)
Siegi
la source
4

Il peut être résolu par le code simple ci-dessous avec Stream and Try dans AbacusUtil :

Stream.of(accounts).filter(a -> Try.call(a::isActive)).map(a -> Try.call(a::getNumber)).toSet();

Divulgation : Je suis le développeur de AbacusUtil.

user_3380739
la source
3

En étendant la solution @marcg, vous pouvez normalement lancer et intercepter une exception vérifiée dans Streams; c'est-à-dire que le compilateur vous demandera de capturer / relancer comme vous étiez en dehors des flux !!

@FunctionalInterface
public interface Predicate_WithExceptions<T, E extends Exception> {
    boolean test(T t) throws E;
}

/**
 * .filter(rethrowPredicate(t -> t.isActive()))
 */
public static <T, E extends Exception> Predicate<T> rethrowPredicate(Predicate_WithExceptions<T, E> predicate) throws E {
    return t -> {
        try {
            return predicate.test(t);
        } catch (Exception exception) {
            return throwActualException(exception);
        }
    };
}

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

Ensuite, votre exemple serait écrit comme suit (en ajoutant des tests pour le montrer plus clairement):

@Test
public void testPredicate() throws MyTestException {
    List<String> nonEmptyStrings = Stream.of("ciao", "")
            .filter(rethrowPredicate(s -> notEmpty(s)))
            .collect(toList());
    assertEquals(1, nonEmptyStrings.size());
    assertEquals("ciao", nonEmptyStrings.get(0));
}

private class MyTestException extends Exception { }

private boolean notEmpty(String value) throws MyTestException {
    if(value==null) {
        throw new MyTestException();
    }
    return !value.isEmpty();
}

@Test
public void testPredicateRaisingException() throws MyTestException {
    try {
        Stream.of("ciao", null)
                .filter(rethrowPredicate(s -> notEmpty(s)))
                .collect(toList());
        fail();
    } catch (MyTestException e) {
        //OK
    }
}
PaoloC
la source
Cet exemple ne compile pas
Roman M
Salut @RomanM merci d'avoir indiqué ceci: j'ai corrigé le type de retour manquant sur la méthode "throwActualException". Nous l'utilisons en production donc j'espère que cela fonctionne aussi de votre côté.
PaoloC du
3

Pour ajouter correctement le code de gestion IOException (à RuntimeException), votre méthode ressemblera à ceci:

Stream<Account> s =  accounts.values().stream();

s = s.filter(a -> { try { return a.isActive(); } 
  catch (IOException e) { throw new RuntimeException(e); }});

Stream<String> ss = s.map(a -> { try { return a.getNumber() }
  catch (IOException e) { throw new RuntimeException(e); }});

return ss.collect(Collectors.toSet());

Le problème maintenant est que le IOExceptiondevra être capturé en tant que RuntimeExceptionet reconverti en un IOException- et cela ajoutera encore plus de code à la méthode ci-dessus.

Pourquoi l'utiliser Streamquand cela peut être fait comme ça - et la méthode lance IOExceptiondonc aucun code supplémentaire n'est nécessaire pour cela aussi:

Set<String> set = new HashSet<>();
for(Account a: accounts.values()){
  if(a.isActive()){
     set.add(a.getNumber());
  } 
}
return set;
Le coordinateur
la source
1

En gardant ce problème à l'esprit, j'ai développé une petite bibliothèque pour traiter les exceptions vérifiées et les lambdas. Les adaptateurs personnalisés vous permettent d'intégrer les types fonctionnels existants:

stream().map(unchecked(URI::new)) //with a static import

https://github.com/TouK/ThrowingFunction/

Grzegorz Piwowarek
la source
1

Votre exemple peut être écrit comme suit:

import utils.stream.Unthrow;

class Bank{
   ....
   public Set<String> getActiveAccountNumbers() {
       return accounts.values().stream()
           .filter(a -> Unthrow.wrap(() -> a.isActive()))
           .map(a -> Unthrow.wrap(() -> a.getNumber()))
           .collect(Collectors.toSet());
   }
   ....
}

La classe Unthrow peut être prise ici https://github.com/SeregaLBN/StreamUnthrower

SeregaLBN
la source
0

Si cela ne vous dérange pas d'utiliser des bibliothèques tierces, la bibliothèque cyclops-react d'AOL ,closure :: Je suis un contributeur, a une classe ExceptionSoftener qui peut vous aider ici.

 s.filter(softenPredicate(a->a.isActive()));
John McClean
la source
0

Les interfaces fonctionnelles de Java ne déclarent aucune exception cochée ou décochée. Nous devons changer la signature des méthodes de:

boolean isActive() throws IOException; 
String getNumber() throwsIOException;

À:

boolean isActive();
String getNumber();

Ou manipulez-le avec le bloc try-catch:

public Set<String> getActiveAccountNumbers() {
  Stream<Account> s =  accounts.values().stream();
  s = s.filter(a -> 
    try{
      a.isActive();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  Stream<String> ss = s.map(a -> 
    try{
      a.getNumber();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  return ss.collect(Collectors.toSet());
}

Une autre option consiste à écrire un wrapper personnalisé ou à utiliser une bibliothèque telle que ThrowingFunction. Avec la bibliothèque, il suffit d'ajouter la dépendance à notre pom.xml:

<dependency>
    <groupId>pl.touk</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.3</version>
</dependency>

Et utilisez les classes spécifiques telles que ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier.

À la fin, le code ressemble à ceci:

public Set<String> getActiveAccountNumbers() {
  return accounts.values().stream()
    .filter(ThrowingPredicate.unchecked(Account::isActive))
    .map(ThrowingFunction.unchecked(Account::getNumber))
    .collect(Collectors.toSet());
}
SHoko
la source
0

Je ne vois aucun moyen de gérer les exceptions vérifiées dans le flux (Java -8), la seule façon dont j'ai appliqué est de capturer les exceptions vérifiées dans le flux et de les relancer comme exception non vérifiée.

        Arrays.stream(VERSIONS)
        .map(version -> TemplateStore.class
                .getClassLoader().getResourceAsStream(String.format(TEMPLATE_FILE_MASK, version)))
        .map(inputStream -> {
            try {
                return ((EdiTemplates) JAXBContext.newInstance(EdiTemplates.class).createUnmarshaller()
                        .unmarshal(inputStream)).getMessageTemplate();
            } catch (JAXBException e) {
                throw new IllegalArgumentException(ERROR, e);
            }})
        .flatMap(Collection::stream)
        .collect(Collectors.toList());
atul sachan
la source
Essayez-vous réellement de répondre à la question?
Nilambar Sharma
@Nilambar - Ce que j'essaie de dire ici, c'est qu'il n'y a aucun moyen de gérer l'exception vérifiée tout en utilisant le flux java ... à la fin de tout ce dont nous avons besoin pour attraper celui vérifié et lancer celui qui n'est pas coché au moment de l'exécution .. Maintenant il y a deux choses, 1. si vous pensez que je ne suis pas correct avec ma compréhension, veuillez me corriger ou 2. si vous pensez que mon message n'est pas pertinent, je suis heureux de supprimer le même. salutations, Atul
atul sachan
0

Si vous souhaitez gérer l'exception dans le flux et continuer à traiter les autres, il existe un excellent article dans DZone de Brian Vermeer utilisant le concept de l'un ou l'autre. Cela montre une excellente façon de gérer cette situation. La seule chose qui manque est un exemple de code. Ceci est un exemple de mon exploration en utilisant les concepts de cet article.

@Test
public void whenValuePrinted_thenPrintValue() {

    List<Integer> intStream = Arrays.asList(0, 1, 2, 3, 4, 5, 6);
    intStream.stream().map(Either.liftWithValue(item -> doSomething(item)))
             .map(item -> item.isLeft() ? item.getLeft() : item.getRight())
             .flatMap(o -> {
                 System.out.println(o);
                 return o.isPresent() ? Stream.of(o.get()) : Stream.empty();
             })
             .forEach(System.out::println);
}

private Object doSomething(Integer item) throws Exception {

    if (item == 0) {
        throw new Exception("Zero ain't a number!");
    } else if (item == 4) {
        return Optional.empty();
    }

    return item;
}
Steve Gelman
la source