c'est ce que je disais, on peux chainer l'exception
bon je n'ai pas pu résister au plaisir (pervers) du hack au sujet des exceptions (attention contient du java 8)
Bon jusque là on a juste un Consumer qui a des problèmes d'exceptions
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 @FunctionalInterface public interface PeskyConsumer<X,T extends Throwable> { public void accept(X arg) throws T ; }
maintenant travaux pratiques :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 //d'accord il y a de l'abus ce n'est pas une interface mais une classe avec uniquement des méthodes statiques public interface Teflon { public default <X, T extends Throwable> Consumer<X> teflonize(final PeskyConsumer<X,T> consumer) { return new Consumer<X>() { @Override public void accept(X t) { try { consumer.accept(t); } catch (Throwable exc) { Teflon.<RuntimeException>forgetThrowsClause(exc); } } } ; } public static <T extends Throwable> T forgetThrowsClause(Throwable t) throws T{ throw (T) t; } }
de plus en plus compliqué (sans augmentation du prix des places!)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 // code inutile (on pourrait directement appeler en "teflonizant") class PlasticArrayList<T> extends ArrayList<T> implements Teflon{ public void forEachPesky(PeskyConsumer<T,?> pesk) { forEach(teflonize(pesk)); } }
comme quoi: pourquoi faire simple quand on peut faire crade ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 @FunctionalInterface public interface MayFail { public void check() throws Exception ; } public class PotentialFail implements MayFail{ boolean fail ; static int num ; int number = num++ ; public PotentialFail(boolean fail) { this.fail = fail; } @Override public void check() throws Exception { System.out.println("call " +this); if(fail) { throw new Exception("failed" +this) ; } } public String toString() { return "PotentailFail " + number ; } } public class TestSneakyConsumer { public static void main(String[] args) { PlasticArrayList<PotentialFail> list = new PlasticArrayList<>() ; list.add(new PotentialFail(false)) ; list.add(new PotentialFail(true)) ; list.add(new PotentialFail(false)) ; try { list.forEachPesky(MayFail::check); } catch(Exception exc) { System.err.println("got Exception" +exc); } } }
Attends là, j'ai bien lu, tu as sod***sé à sec le compilateur et la jvm en déguisant un Throwable quelconque en RuntimeException?
Je savais qu'il était possible de s'affranchir de l'obligation de catch dans la jvm, via des modifications du bytecode ou l'utilisation d'autres langages, mais pas que c'était possible juste en jouant avec les generic
Bravo! Tu as gagné le prix du pyscodepathe du mois
On commence comme ça et on fini par pondre du code pour ajouter dynamiquement des valeurs dans les enum ou pour construire un objet sans jamais appeler son constructeur
Je suis d'accord avec le fait qu'on souhaite parfois le retour de la sainte inquisition
en fait ce hack a été publié il y a 5 ans mais avec les objets de java.util.function on a un problème et là on peut (peut-être) faire un usage de ce truc qui ne nous garantisse pas une place en enfer ... voici , il me semble, un code, plus propre :
ici l'obligation de faire un catch est juste reportée d'un cran.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 public class Cocoon { static <T extends Throwable> T forgetThrowsClause(Throwable t) throws T{ throw (T) t; } public static <X, T extends Throwable> Consumer<X> consumer(PeskyConsumer<X,T> peskyConsumer) throws T { return new Consumer<X>() { @Override public void accept(X t) { try { peskyConsumer.accept(t); } catch (Throwable exc) { Cocoon.<RuntimeException>forgetThrowsClause(exc) ; } } } ; } // etc, etc.. avec les principaux codes de java.util.function : Function, Supplier, ...
@professeur shadoko : je trouve qu'il y a quelques soucis avec ce "hack" :
- Cela cache le throw au compilateur, ce qui peut être gênant dans certains cas (le compilateur ne détecte pas la sortie de la méthode).
- Mais surtout on ne peut pas catcher l'exception simplement, car le compilateur va nous empêcher de faire un try/catch sur une checked-exception si elle n'est pas déclarée dans les méthodes...
Perso je préfère quand même les englober dans une RuntimeException, voir une exception perso avec une méthode fillInStackTrace() vide pour éviter le double stacktrace...
Par contre il est clair que les checked-exceptions deviennent (encore plus) une plaie avec les lambdas.
a++
reprenons le deuxième cas (Cocoon) : la méthode de masquage des exceptions n'est pas publique
on ne cache que provisoirement le catch au compilateur et l'exception remonte correctement
c'est comme si on disait: provisoirement j'ai une petite partie de code dans laquelle je ne peux retranscrire les clauses obligatoires (throws) mais :
- les exceptions vont remonter correctement (et interrompre un traitement si nécessaire)
- en bout de chaine je reinstalle l'obligation de faire un catch
j'ai donc une zone de code dans laquelle je neutralise l'obligation de clause throws (parceque les librairies de fonction ne le prévoient pas) mais je remets les choses proprement (sans rajouter une RuntimeException d'emballage)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 try { /// try/catch obligé! Consumer<PotentialFail> cons = Cocoon.consumer(PotentialFail::check); list.forEach(cons); } catch(Exception exc) { System.err.println("got Exception" +exc); } // ici pas de try catch (toString est stupide : c'est juste pour montrer que le compilo n'exige pas de try/catch) Consumer<PotentialFail> cons = Cocoon.consumer(PotentialFail::toString); list.forEach(cons);
c'est un utilitaire à destination d'autres programmeurs
Le problème n'est pas selon moi les lambdas, mais surtout le fait qu'à aucun moment il semblerais, les apis stream n'aient envisagé qu'il puisse être vaguement possible qu'un machin de la chaine lance une exception
D'ailleurs, si je lance une RuntimeException dans un parallelStream, il se passe quoi? Est-ce que la spec précise quoi que ce soit à ce sujet? Les autres threads continuent de consommer, tout s'arrête? quoi?
Il peut tout à fait y avoir une exception dans un Stream... du moment que c'est une RuntimeException car l'API ne déclare pas d'exception.
(ou qu'on utilise un hack comme celui du professeur shadoko)
Le problème c'est uniquement cette limitation des checked-exceptions qui impose de déclarer les exceptions.
Il aurait fallut rajouter des "throws Exception" partout pour que cela puisse fonctionner avec des checked-exception, mais cela aurait eu l'effet pervers de devoir de trainer des try/catch partout même lorsqu'il n'y en a pas besoin !
Une autre solution envisagé à un moment consistait à faire évoluer l'API des Generics pour qu'elle puissent gérer proprement des exceptions en paramétrage... mais cela emmenait pas mal de soucis et de complexité...
Ben il n'y a rien de particulier : le traitement est arrêté et tu reçois une exception.
Les autres threads sont bien sûr stoppé (mais sauf erreur c'est l'API ForkJoin de Java 7 qui doit gérer cela).
a++
est-ce qu'ici le problème n'est pas plutôt ce que fait l'agent situé dans le stream?
Si c'est un filtre le code devrait lui-même s'occuper de l'exception et retirer (ou invalider d'une certaine manière) l'objet coupable du stream ....
idée à demi-cuite: gérer alors un flot d'exceptions à part (j'ai un peu ça dans une gestion d'exception de démarrages de matériel: on essaye de démarrer tous les matériels qui sont indépendants les uns des autres et le code reçoit à la fin une Exception qui est en fait une séquence d'exceptions: le matériel A2 a ça, le matériel B5 a ça, le matériel C7 a ça , etc....- ce ne sont pas des "causes" au sens Java puisqu'on a des incidents indépendants qui sont collectés dans une même opération-)
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager