IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Programmation fonctionnelle en Java


Sujet :

Java

  1. #1
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2018
    Messages : 2
    Points : 3
    Points
    3
    Par défaut Programmation fonctionnelle en Java
    Hello world!
    J'aimerais votre avis sur le fait de faire de la programmation fonctionnelle en Java. Il existe depuis Java 8 des interfaces et des fonctions lambda mais je trouve leur utilisations un peu pénible.

    En ayant suivi ce tuto sur ce site:
    https://dzone.com/articles/functiona...ramming-java-8

    J'en suis arrivé à expérimenter l'implémentation de quelques fonctions triviales.
    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
     
    package univ.cours.sio;
     
     
    import java.util.function.BinaryOperator;
    import java.util.function.Function;
    import java.util.function.UnaryOperator;
     
    import univ.cours.utils.Utils;
     
    /**
     * fonctional skills in java 
     */
    public class App {
        public static void main( String[] args ) {
        	Function<Integer, UnaryOperator<Integer>> makeAdder = Utils::adder;
        	Function<Integer, UnaryOperator<Integer>> makeMultiplier = Utils::multiplier;
     
        	UnaryOperator<Integer> add1 = makeAdder.apply(1);
        	UnaryOperator<Integer> multiply5 = makeMultiplier.apply(5);
     
        	BinaryOperator<UnaryOperator<Integer>> compose = (f, g) -> x -> g.apply(f.apply(x)); 
     
        	UnaryOperator<Integer> operation = compose.apply(add1, multiply5);
     
        	Function<String, Integer> atoi = s -> Integer.valueOf(s);
     
        	Integer result = operation.apply(10);
     
            System.out.println(result);
        }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    package univ.cours.utils;
     
    import java.util.function.UnaryOperator;
     
    public class Utils {
    	public static UnaryOperator<Integer> adder(Integer y) {
    		return x -> x + y;
    	}
     
    	public static UnaryOperator<Integer> multiplier(Integer y) {
    		return x -> x * y;
    	}
    }
    Je sais que la programmation fonctionelle est un paradigme assez pratique. J'aime beaucoup le fait de respecter l'immutabilité des objets (philosophie adopté par certains framework comme React), de chainé les appels de méthodes (map, filter, reduce) et de déclarer des fonctions anonymes ect.
    Je sais que le langage Scala est parfaitement adapté, mais j'aimerais tout de même pouvoir faire du fonctionnel en Java.

    Faites-vous de la programmation fonctionnelle avec Java ou utilisez-vous plutôt un langage plus adapté comme Scala ?

    Votre retour d'expérience m'intéresse.

  2. #2
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,

    Mon plus gros problème avec la programmation fonctionnelle sous Java, c'est la gestion des exceptions.
    Cela s'adapte assez mal avec la notion de "checked" exception de Java.

    Du coup il fait parfois bidouiller pour simplement remonter l'exception.

  3. #3
    Membre confirmé
    Inscrit en
    Mai 2007
    Messages
    335
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 335
    Points : 511
    Points
    511
    Par défaut
    Citation Envoyé par adiGuba Voir le message
    Salut,

    Mon plus gros problème avec la programmation fonctionnelle sous Java, c'est la gestion des exceptions.
    Cela s'adapte assez mal avec la notion de "checked" exception de Java.

    Du coup il fait parfois bidouiller pour simplement remonter l'exception.
    De plus en plus on programme en RuntimeException, en tout cas je l'ai vu sur certains frameworks, on peut wrapper simplement nos exception dans une ou plusieurs Runtime Custom (faire ses propres implem pour typer finement) en gardant la cause comme origine si c'est une checked. Même en programmation classique, se trainer la checked depuis toutes les couches d'interface DAO jusqu'au couches services ou écrans, c'est finalement assez inutile.

  4. #4
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Oui mais si on doit utiliser une API qui utilise des checked-exceptions (et il y en a beaucoup) on se retrouve à devoir faire des try/catch dans tous les sens et encapsuler les exceptions.

    C'edt particulièrement fastidieux, illisible, et encore moins facile de gérer les erreurs...

  5. #5
    Membre chevronné
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 75
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Points : 1 855
    Points
    1 855
    Par défaut
    Citation Envoyé par deltree Voir le message
    . Même en programmation classique, se trainer la checked depuis toutes les couches d'interface DAO jusqu'au couches services ou écrans, c'est finalement assez inutile.
    C'est une opinion assez répandue ... mais ce n'est pas la mienne (même sur le dernier gros projet, très complexe, sur lequel j'ai travaillé ça permettait au contraire de nous forcer à réfléchir à des dispositifs intermédiaires très propres - bon d'acc: il y avait très peu de code sous forme de "flow"-).
    J'ai des principes: je peux toujours trouver une bonne raison pour les contredire .... mais j'ai des principes!
    (mon excellent bouquin sur Java : https://eska-publishing.com/fr/livre...822407076.html)

  6. #6
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    J'ai une préférence personnelle pour les unchecked-exceptions, mais les checked-exceptions n'avait jamais été un gros problème pour moi jusque là.

    Avec les interfaces fonctionnelles cela s'avère souvent fastidieux.


    Exemple tout bête : ce code là ne peut pas marcher car Files.lines() peut remonter une IOException.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	public Set<String> getAllWords(Path directory) throws IOException {
    		Pattern separator = Pattern.compile("\\W+");
    		return Files.walk(directory)  // On recherche tous les fichier du répertoire
    			.filter(p -> p.getFileName().toString().endsWith(".java")) // Avec l'extension .java
    			.flatMap(p -> Files.lines(p, StandardCharsets.UTF_8)) // Dont on lit toutes les lignes
    			.map(String::trim) // On supprime les blancs
    			.filter(s -> !s.isEmpty()) // On ignore les lignes vides
    			.flatMap(line -> separator.splitAsStream(line)) // Et on y recherche toutes les mots
    			.collect(Collectors.toSet()); // Que l'on rajoute dans un Set
    	}
    Il faut impérativement traiter l'exception, ce qui alourdit considérablement la chose :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    .flatMap(p -> Files.lines(p, StandardCharsets.UTF_8)) // Dont on lit toutes les lignes
    Devient :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    .flatMap(p -> {
       try {
           return Files.lines(p, StandardCharsets.UTF_8);
       } catch (IOException e) {
           throw new UndeclaredThrowableException(e);
       }
    }) // Dont on lit toutes les lignes
    En plus d'être bien lourd, je trouve le code moins propre puisqu'il ne remonte plus directement une IOException.
    Si je veux que ce soit bien propre et que ca me remonte une IOException, je dois retraiter l'exception :

    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 Set<String> getAllWords(Path directory) throws IOException {
    		Pattern separator = Pattern.compile("\\W+");
    		try {
    			return Files.walk(directory)  // On recherche tous les fichier du répertoire
    				.filter(p -> p.getFileName().toString().endsWith(".java")) // Avec l'extension .java
    				.flatMap(p -> {
    					try {
    						return Files.lines(p, StandardCharsets.UTF_8);
    					} catch (IOException e) {
    						throw new UneUncheckedExceptionPerso(e);
    					}
    				}) // Dont on lit toutes les lignes
    				.map(String::trim) // On supprime les blancs
    				.filter(s -> !s.isEmpty()) // On ignore les lignes vides
    				.flatMap(line -> separator.splitAsStream(line)) // Et on y recherche toutes les mots
    				.collect(Collectors.toSet()); // Que l'on rajoute dans un Set
    		} catch (UneUncheckedExceptionPerso e) {
    			throw e.getOriginalIOException();
    		}
    	}

    Pour peu qu'il y ait plusieurs unchecked-exception ca devient atroce !

  7. #7
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 937
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 937
    Points : 4 358
    Points
    4 358
    Par défaut
    Il existe sur le Net plusieurs versions de classes qui servent à relancer des exceptions sans devoir les déclarer :
    Par exemple
    http://www-sop.inria.fr/oasis/proact...ower.java.html
    basée sur javassist,

    mais on peut le faire aussi avec ByteBuddy

    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
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
     
    interface Thrower {
        public void throwException(final Throwable t);
    }
     
    public class ExceptionThrower {
        private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
     
        enum ThrowThrowable implements StackManipulation {
     
            INSTANCE; // singleton
     
            @Override
            public boolean isValid() {
                return true;
            }
     
            @Override
            public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
                methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
                methodVisitor.visitInsn(Opcodes.ATHROW);
                return new Size(1, 2);
            }
        }
     
        static enum ThrowExceptionMethod implements ByteCodeAppender {
            INSTANCE;
     
            @Override
            public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
                StackManipulation.Size operandStackSize = new StackManipulation.Compound(
                        ThrowThrowable.INSTANCE).apply(methodVisitor, implementationContext);
     
                return new Size(operandStackSize.getMaximalSize(), instrumentedMethod.getStackSize());
            }
     
        }
     
        static enum ThrowExceptionImpl implements Implementation {
            INSTANCE;
     
            @Override
            public InstrumentedType prepare(InstrumentedType instrumentedType) {
                return instrumentedType;
            }
     
            @Override
            public ByteCodeAppender appender(Target implementationTarget) {
                return ThrowExceptionMethod.INSTANCE;
            }
        }
     
        private static Thrower thrower = null;
     
        private static void loadClassByteBuddy() {
            try {
                @SuppressWarnings("unchecked")
                Class<Thrower> dynamicType = (Class<Thrower>) new ByteBuddy().with(new NamingStrategy.AbstractBase() {
     
                    @Override
                    protected String name(TypeDescription superClass) {
     
                        return ExceptionThrower.class.getPackage().getName() + ".TheActualExceptionThrower";
                    }
                })
                        .subclass(Thrower.class, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR)
                        .method(ElementMatchers.named("throwException"))
                        .intercept(ThrowExceptionImpl.INSTANCE)
                        .make().load(ExceptionThrower.class.getClassLoader())
                        .getLoaded();
     
                thrower = dynamicType.newInstance();
            }
            catch (final Exception e) {
                LOGGER.error("Internal error initializing Thrower with ByteBuddy");
            }
        }
     
        /* The first time the mechanism is used, it has to initialize the thrower */
        private static void activate() {
            loadClassByteBuddy();
        }
     
        public synchronized static void throwException(final Throwable t) {
            if (thrower == null) {
                activate();
            }
     
            if (thrower != null) {
                thrower.throwException(t);
            }
        }
    }
    Avec ce genre de classe vous ne devez pas envelopper l'exception originale pour la relancer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    
    catch (IOException e) {
        ExceptionThrower.throwException(e);
    }

  8. #8
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    C'est quand même sortir l'artillerie lourde... 😕

    Au passage il existe aussi des solutions plus simple basé sur les Générics en jouant avec l'erasure..

    Mais c'est pas forcément mieux de mon point de vue :

    • Il faut quand même utiliser un try/catch pour intercepter l'exception et appeller cette méthode de rethrow...
    • Le rethrow de l'exception n'est pas visible par le compilateur, ce qui fait qu'il faut parfois bidouiller en rajoutant un return ou un throw bidon...
    • On ne peut plus catcher l'exception facilement. Le compilateur refusera de nous laisser utiliser un try/catch avec une checked-exception si elle n'est déclarée dans aucune des méthodes...

  9. #9
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 937
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 937
    Points : 4 358
    Points
    4 358
    Par défaut
    Et le code utilisant les generics est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    public final class ExceptionThrower{
        private ExceptionThrower(){}
     
        public static void throwException(final Throwable ex){
            ExceptionThrower.<RuntimeException>throwsUnchecked(ex);
        }
     
        private static <T extends Throwable> void throwsUnchecked(final Throwable toThrow) throws T {
            throw (T) toThrow;
        }
    }

Discussions similaires

  1. Réponses: 1
    Dernier message: 23/12/2017, 17h03
  2. Réponses: 5
    Dernier message: 30/08/2006, 09h09
  3. Réponses: 3
    Dernier message: 07/11/2005, 17h33
  4. Réponses: 2
    Dernier message: 08/09/2005, 12h18
  5. [JDBC] Programmation autre que Java
    Par Vow dans le forum JDBC
    Réponses: 2
    Dernier message: 23/06/2004, 11h22

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo