Un an s'est écoulé depuis la publication, je n'ai donc pas changé l'essence du contenu, je l'ai revu et mis à jour en me concentrant sur l'ajout d'explications sur la source de la présentation.
Cela fait plus de 4 ans que Java 8 a été publié en mars 2014, et il a été utilisé par de nombreuses personnes, notamment Java 9 et Java 10 et versions ultérieures. La programmation fonctionnelle est devenue un sujet brûlant lors de la sortie de Java 8. De nos jours, le terme programmation fonctionnelle est généralement reconnu et répandu parmi les développeurs de systèmes. Cependant, il semble que la pénétration réelle soit encore loin, car la situation actuelle est que nous ne voyons pas fréquemment les résultats du développement du système de la programmation fonctionnelle. Et, comme avec Java 8, les langages existants sont également améliorés en incorporant un style de programmation fonctionnel. Je ne peux pas prédire à quoi ressemblera le flux à l'avenir, mais je suppose personnellement que l'utilisation du style de programmation fonctionnelle dans les langages existants deviendra le courant dominant. Je pense qu'un langage fonctionnel pur sera un usage spécialisé, et dans le système Java, Scala ne remplacera pas le langage Java. La programmation fonctionnelle semble être dans une telle situation, mais on s'attend à ce qu'elle se propage d'une manière ou d'une autre à l'avenir, j'ai donc fait une étude générale et essayé de pratiquer la programmation simple dans l'environnement Java 10. .. De plus, j'ai décidé d'afficher les résultats d'apprentissage sous forme de mémorandum, pensant que cela pourrait être utile pour ceux qui sont intéressés. Veuillez noter que le contenu peut avoir une partie subjective ou un style de programme qui inclut une orientation personnelle. Pour les mots et expressions spécifiques à la programmation des fonctions, veuillez vous reporter aux brèves explications comprises dans la plage comprise à la fin. (* Mots avec des nombres)
Java 8 ajoute les nouvelles fonctionnalités suivantes pour la programmation de fonctions:
Avant d'entrer dans ces introductions, je voudrais essayer d'expliquer brièvement ce qu'est la programmation fonctionnelle en premier lieu.
Au sens strict, la programmation fonctionnelle est «une méthode de construction d'un programme avec des fonctions qui ont une transparence de référence (* 9) et aucun effet secondaire (* 8)», et cette définition est basée sur un langage fonctionnel. Cependant, il est maintenant possible de l'implémenter dans le style de programmation fonctionnelle même dans les langages existants, et ici nous avons décidé d'avoir une vision large de la programmation fonctionnelle et avons répertorié trois fonctionnalités.
Je pense que créer un programme avec la méthode et le but mentionnés ci-dessus peut être reconnu comme une programmation fonctionnelle dans une large perspective. De plus, la programmation fonctionnelle n'est pas liée aux méthodes de développement telles que l'analyse des exigences et les processus de conception comme orienté objet, mais est destinée au processus de mise en œuvre du programme lui-même. Afin d'effectuer la programmation fonctionnelle, il y a ceux qui prennent en charge au niveau du langage comme Haskell et Scala, et ceux qui ajoutent une API pour la programmation fonctionnelle à un langage existant tel que Java 8. On dit que Java 8 peut couvrir la plage de 1 à 2, et Haskell et Scala peuvent couvrir la plage de 1 à 3. Un langage de programmation qui peut presque éviter les effets secondaires est également appelé un langage de programmation purement fonctionnel, et de ce point de vue, Scala's 3. est considéré comme un quasi-trois plutôt qu'un pur 3. Cependant, il va sans dire que cela dépend du projet s'il est nécessaire de configurer le système avec des «fonctions qui évitent presque les effets secondaires». La programmation fonctionnelle utilise une méthode pour créer une fonction relativement petite et l'appliquer ou la combiner pour développer un système basé sur le format de programmation déclarative (* 3). Par conséquent, pour mettre en œuvre le niveau 3, il est nécessaire de bien comprendre le concept de langage fonctionnel et de programmation fonctionnelle plutôt que la méthode de programmation orientée objet conventionnelle. En outre, il est nécessaire de bien comprendre à l'avance dans quelle mesure le problème cible correspond à la programmation fonctionnelle, telle que le système lié, le middleware de la plate-forme, le cadre de la plate-forme, les actifs accumulés associés et l'environnement de développement / gestion / opération. il y a. Les éléments suivants sont généralement répertoriés comme les avantages de la programmation fonctionnelle.
Il semble y avoir des situations au cas par cas pour les 4e et 5e items, mais il semble certain que la quantité de programmation sera réduite pour les parties applicables. D'autre part, l'inconvénient est que si le système de développement peut être exprimé mathématiquement, il est théoriquement possible de le développer avec une programmation de fonction pure, mais en réalité, il existe peu d'environnements de ce type, en particulier le traitement CRUD dans les systèmes d'entreprise. On dit qu'il n'y a pas beaucoup de mérite dans l'application principale. Java8 introduit de nouveaux mécanismes tels que les interfaces fonctionnelles, les expressions lambda, les flux et facultatif pour prendre en charge la programmation fonctionnelle. Cela permet d'écrire une partie de la programmation pédagogique traditionnelle dans un style de programmation fonctionnelle (programmation déclarative). Et lors de la programmation fonctionnelle avec ce Java 8 ou version ultérieure,
Je pense qu'il y a deux cas, mais personnellement, je pense qu'il vaut mieux faire des pas (1) → (2) pour apprendre. Une chose à garder à l'esprit est que la programmation fonctionnelle ne fonctionne que sur Java 8 et supérieur. Afin de pouvoir fonctionner avec des versions plus anciennes de Java telles que Java6 et Java7, il est nécessaire de développer avec la programmation pédagogique comme auparavant, pas avec la programmation fonctionnelle.
Les principales méthodes nouvelles et mises à jour liées à la programmation fonctionnelle dans Java 8 sont les suivantes. Ces méthodes sont utilisées pour tirer parti des fonctionnalités de base de la programmation fonctionnelle. Je pense que cela peut être exprimé comme l'incorporation de la programmation fonctionnelle dans un langage orienté objet. Vous pouvez facilement trouver les détails de chacune de ces méthodes en ligne ou dans des livres. Ici, nous donnerons un aperçu de chaque méthode et un échantillon de méthodes typiques.
・ Interface fonctionnelle Les expressions Lambda et les références de méthode (* 7) peuvent être utilisées à l'aide de l'interface fonctionnelle. Vous pouvez créer les vôtres, mais plusieurs interfaces fonctionnelles standard sont disponibles.
(): Nom de la méthode principale, type d'argument → type de retour Ce qui suit est une définition simple et un exemple d'utilisation d'une interface de type de fonction personnalisée.
[Description de code associé à l'exemple d'interface] -Un programme qui spécifie un nom de passe-temps et affiche "mon passe-temps: nom de passe-temps". -L'interface fonctionnelle IHobby avec la méthode abstraite getHobby (valeur de retour: chaîne de caractères) est définie de 009 à 012. -En 003 à 005, l'implémentation du traitement de IHobby est définie à l'aide de l'expression lambda. -La méthode getHobby de IHobby est exécutée en 006.
InterfaceExample
001 public class InterfaceExample{
002     public static void main(String[] args){
003     IHobby hb = (String hobby) -> {
004       return "my hobby : " + hobby;
005     };
006     System.out.println(hb.getHobby("cycling"));
007   }
008 }
009 @FunctionalInterface
010 interface IHobby{
011   public String getHobby(String hobby);
012 }
Vous trouverez ci-dessous un exemple simple d'utilisation de l'interface fonctionnelle standard Function.
[Description du code lié à LambdaParameter] -Un programme qui multiplie la valeur spécifiée par 5 et la produit. -La méthode getFiveTimes (valeur de retour: type entier) qui multiplie la valeur par 5 est définie entre 016 et 018. -La méthode d'exécution est exécutée de 007 à 009 pour obtenir la valeur 5 fois. -L'expression lambda du deuxième argument défini en 012 de 007 à 009 est reçue par l'interface fonctionnelle. -La méthode apply de l'interface fonctionnelle est exécutée en 013.
LambdaParameter
001 package functiontest;
002 import java.io.IOException;
003 import java.util.function.Function;
004 public final class LambdaParameter {
005   public static void main(String[] args) throws IOException {
006     Integer value = 1;
007     Integer res = execute(value, t -> {
008       return getFiveTimes(t);
009     });
010     System.out.println("résultat:" + res);
011   }
      /**
      *Interface de la fonction d'argument(Style Lambda)Méthode à exécuter
      */
012   public static <R> R execute(Integer value, Function<Integer, R> fnc) {
013     R rtnval = fnc.apply(value);
014     return rtnval;
015   }
      /**
      *Méthode pour exécuter le traitement du béton(Multipliez la valeur par 5)
      */
016   public static Integer getFiveTimes(Integer value) {
017     return value * 5;
018   }
019 }
・ Carte De nouvelles méthodes ont également été ajoutées à la classe Map existante. Les principales méthodes ajoutées sont forEach, replaceAll, computeIfAbsent, computeIfPresent, compute et merge.
Voici un exemple simple. (k: clé de mappage v: valeur de mappage p: clé de valeur d'argument: clé spécifiée)
[Description du code associé à la carte] Le type de carte est String, String. ・ ForEach (001): Sort tous les éléments de la carte au format [Clé: Valeur]. ・ ReplaceAll (002 ~): Remplacez les valeurs de tous les éléments de la carte par null → [clé] et non nul → [clé + 2 premiers caractères de valeur]. ・ ComputeIfAbsent (008 ~): a. Si la clé existe dans la clé de mappage La valeur n'est pas mise à jour et la valeur est la valeur de retour. b. Si la clé existe et que la valeur est nulle La valeur est mise à jour en [clé + "-addition"] et cette valeur est la valeur de retour. c. S'il n'existe pas dans la clé Ajoutez une valeur avec la clé et la valeur [clé + "-addition"] au mappage, et cette valeur sera la valeur de retour. ・ ComputeIfPresent (011 ~): a. Si la clé existe dans la clé de mappage La valeur est mise à jour en [Clé + Valeur + "-addition"] et cette valeur est la valeur de retour. b. Si la clé existe et que la valeur est nulle La valeur n'est pas mise à jour et null est la valeur de retour. c. S'il n'existe pas dans la clé La valeur n'est ni ajoutée ni mise à jour et null est la valeur de retour. ・ Calculer (014 ~): a. Si la clé existe dans la clé de mappage La valeur est mise à jour en [Clé + Valeur + "-addition"] et cette valeur est la valeur de retour. b. Si la clé existe et que la valeur est nulle La valeur est mise à jour en [clé + valeur (null) + "-addition"], et cette valeur est la valeur de retour. c. S'il n'existe pas dans la clé Ajoutez l'élément (clé, [clé + valeur (null) + "-addition"]) à la carte, et cette valeur sera la valeur de retour. ・ Fusionner (017 ~): a. Si la clé existe dans la clé de mappage La valeur est mise à jour en [Value + "-add"] et cette valeur est la valeur de retour. b. Si la clé existe et que la valeur est nulle La valeur est mise à jour en ["-add"](en ignorant les valeurs nulles) et cette valeur est la valeur de retour. c. S'il n'existe pas dans la clé Ajoutez l'élément (clé, ["-add"]) (ignorez la valeur nulle) à la carte, et cette valeur sera la valeur de retour.
MapMethod
    forEach:
001   map.forEach((k, v) -> System.out.println(k + ":" + v));
    replaceAll:
002   map.replaceAll((k, v) -> {
003     if (null == v) {
004       return k;
005     }
006     return k + v.substring(0, 2);
007   });
    computeIfAbsent:
008   map.computeIfAbsent(key, k -> {
009     return k + "-addition";
010   });
    computeIfPresent:
011   map.computeIfPresent(key, (k, v) -> {
012     return k + v + "-addition";
013   });
    compute:
014   map.compute(key, (k, v) -> {
015     return k + v + "-addition";
016   });
    merge:
017   map.merge(key, "-add", (v, p) -> v + p);
· Liste De nouvelles méthodes ont également été ajoutées à la classe List existante. Les principales méthodes ajoutées sont forEach, removeIf, replaceAll, stream et parallelStream.
Voici un exemple simple. (v: valeur de la liste valeur: valeur externe)
[Description du code associé à la liste] Le type de liste est String. ・ ForEach (001): Affiche les valeurs de tous les éléments de la liste. ・ RemoveIf (002 ~): Il y a une valeur spécifiée dans la liste: L'élément correspondant est supprimé et true est renvoyé. b. La valeur spécifiée est null: l'élément correspondant n'est pas supprimé et false est renvoyé. c. Aucune valeur spécifiée: l'élément n'est pas supprimé et false est renvoyé. ・ ReplaceAll (008 ~): Valeur non nulle pour tous les éléments: mise à jour avec les 2 premiers caractères de la valeur. b. Valeur nulle pour tous les éléments: la valeur n'est pas mise à jour. ・ Trier (014): Un tri par ordre naturel / valeur maximale nulle est effectué pour tous les éléments. ・ Flux (015): a. Obtenez le flux de la liste. ・ ParallelStream (016): a. Obtenez un flux parallèle de liste.
ListMethod
    forEach:
001   list.forEach(v -> System.out.println(v));
    removeIf:
002   list.removeIf(v -> {
003     if (null == v) {
004       return false;
005     }
006     return v.equals(value);
007   });
    replaceAll:
008   list.replaceAll(v -> {
009     if (null == v) {
010       return v;
011     }
012     return v.substring(0, 2);
013   });
    sort:
014   list.sort(Comparator.nullsLast(Comparator.naturalOrder()));
    stream:
015   Stream sm = list.stream();
    parallelStream:
016   Stream sm = list.parallelStream();
・ API Stream Il s'agit d'une API pour la gestion des tableaux et des collections, et peut être utilisée pour le traitement de données tel que l'agrégation de valeurs. Les méthodes de flux sont classées en deux types, l'un pour le traitement intermédiaire et l'autre pour le traitement de fin, en fonction du contenu de l'opération. Exécutez la méthode de traitement de fin via 0 ou plusieurs méthodes de traitement intermédiaires. Une API Stream primitive est également disponible. Méthode de traitement intermédiaire asDoubleStream、asLongStream、boxed、distinct、filter、flatMap、flatMapToDouble、flatMapToInt、flatMapToLong、limit、map、mapToDouble、mapToInt、mapToLong、mapToObj、onClose、parallel、peek、sequential、skip、sorted、unordered Ce qui suit est une brève description des méthodes typiques.
Voici un exemple simple. (flux: instance de flux v: liste de valeurs de flux: liste externe numlimit: valeur externe)
[Description du code lié au flux (traitement intermédiaire)]
Le type de flux est Integer.
・ Filtre (001 ~):
a. Stream Extrait des multiples de 3 pour tous les éléments et renvoie avec Stream 
StreamMethod1
    filter:
001   stream.filter(v -> {
002     return (v%3) == 0;
003   });
    map:
004   list.stream().map(Paths::get);
    flatMap:
005   list.stream().flatMap(v -> {
006     String[] array = new String[Integer.parseInt(v.substring(1))];
007     Arrays.fill(array, "abc" + v);
008     return Stream.of(array);
009   }).collect(Collectors.toList());
    distinct:
010   stream.distinct();
    sorted:
011   stream.sorted();
    limit:
012   stream.limit(numlimit);
Méthode de résiliation allMatch、anyMatch、average、collect、count、findAny、findFirst、forEach、forEachOrdered、iterator、max、min、noneMatch、reduce、spliterator、sum、summaryStatistics、toArray Voici une brève description des méthodes typiques.
Voici un exemple simple. (flux: instance de flux v: tableau de valeurs de flux: tableau externe ac: accumulateur)
[Description du code lié au flux (traitement de fin)]
・ AllMatch (001 ~):
Le flux est vérifié sous la condition de correspondance totale (0 
StreamMethod2
    allMatch:
001   stream.allMatch(v -> {
002     if ((0 < v) && (10 > v)) {
003       return true;
004     }
005     return false;
006   });
    anyMatch:
007   stream.anyMatch(v -> {
008     return v.equals(5);
009   });
    collect:(Plusieurs types existent)
010   //Exemple 1:Arrays.stream(array).collect(Collectors.toList());
011   //Exemple 2:list.stream().collect(StringBuilder::new, (b, v) -> b.append(v), (b1, b2) -> b1.append(b2));
      //1er argument:Génération d'objet de stockage de résultat, 2ème argument:Stocker la valeur du flux 3ème argument:Jointure d'objet
    count:
012   list.stream().count();
    reduce:(Plusieurs types existent)
013   list.stream().reduce((ac, v) -> ac + v);
    max:
014   list.stream().max(Comparator.naturalOrder());
    min:
015   list.stream().min(Comparator.naturalOrder());
    toArray:
016   Object[] array = list.stream().toArray();
    filter+forEach:
017   stream.filter(v -> {
018     return v%3 == 0;
019   }).forEach(System.out::println);
· Optionnel Facultatif est une classe qui encapsule une valeur. Vous pouvez vérifier les valeurs nulles et les valeurs. Facultatif.ofNullable, Facultatif.empty (), opt.isPresent (), ...
Il existe de nombreux échantillons de plusieurs lignes sur le net et les livres, mais ici je vais vous présenter un exemple de niveau avec une certaine fonction. Cependant, la programmation de fonctions n'est pas quelque chose qui devrait être fait comme ça, mais est créée dans la perspective du codage de référence pour une compréhension rudimentaire. Les fonctions sont la gestion des stocks de produits et se composent de deux classes, la classe principale et la classe FunctionStock. Veuillez noter que la classe FunctionStock présente formellement autant d'exemples d'utilisation d'API que possible, de sorte que la programmation fonctionnelle est effectuée même là où elle n'est pas nécessaire. Aperçu de la classe StockMain: La classe principale de la gestion des stocks. Nous ne faisons pas de programmation fonctionnelle. FunctionStock: une classe qui traite réellement l'inventaire. Je fais de la programmation fonctionnelle avec plusieurs méthodes. IStock: interface de classe de traitement d'inventaire. IStockExecuter: interface de fonction Homebrew pour le traitement des stocks. Veuillez vous référer au commentaire de la classe principale pour la fonction détaillée et le commentaire du plan de méthode. Le type de programmation fonctionnelle utilisé par la marque ☆ est décrit.
[Description du code associé à StockMain] Les cartes qui stockent le nombre de stocks et les configurations d'accessoires sont définies de 013 à 014. De 025 à 036, 6 articles et la quantité de stock initiale sont définis. cd et magazine ont des accessoires. Le traitement spécifique est défini entre 016 et 022. Le livre définit deux processus d'achat et un processus de vente. magazine définit un processus de vente. Enfin, le processus de sortie de la liste d'inventaire est défini. 037 à 046 exécutent le processus d'achat avec la méthode du stock. 047 à 056 exécutent le traitement des ventes avec la méthode de vente. La quantité est négative. 057 à 066 sortent la quantité en stock du produit spécifié par la méthode get. 067 à 076 sortent la liste d'inventaire des produits avec la méthode getList. Le traitement des achats, le traitement des ventes, la sortie de la quantité d'inventaire et la sortie de la liste d'inventaire sont tous exécutés par la méthode d'exécution de FunctionStock.
StockMain.java
001 package stocksample;
002 import java.util.Arrays;
003 import java.util.List;
004 import java.util.Map;
005 import java.util.TreeMap;
    /**
     *Classe principale de gestion des stocks
     *Fonction: processus d'achat(stock), Traitement des ventes(sale), Traitement des contrôles d'inventaire(get), Traitement de la sortie de la liste d'inventaire(getList)
     * (): Nom de la méthode
     *Format de traitement des données de stock: nom de la méthode(Nom du produit,quantité)
     *Accessoires pour chaque produit(Plusieurs possibles, mais chaque quantité est limitée à une)Est attaché, et l'inventaire de ses accessoires est également géré.
     *Processus d'achat: Augmentez le nombre de produits spécifiés en stock par le nombre d'achats. Si le produit désigné n'est pas enregistré, le produit est également enregistré.
     *Traitement des ventes: réduisez le nombre de produits spécifiés en stock du nombre vendu. Cependant, s'il n'y a pas de stock avec accessoires, le traitement sera arrêté.
     *Traitement du contrôle des stocks: génère le nombre de produits spécifiés en stock.
     *Traitement de la sortie de la liste de stock: génère le nombre de stocks de tous les produits. La liste des accessoires est également sortie.
     *Traitement des erreurs: génère un message d'erreur lorsqu'il se produit et annule le traitement ultérieur.
     */
006 public class StockMain {
007   private static Map<String, Integer> stockMap;
008   private static Map<String, List<String>> subStockMap;
009   private static IStock stockObject;
010   private static final boolean funcOption = true; //Version JDK8
      //private static final boolean funcOption = false; //JDK6,Version JDK7
011   public static void main(String[] args) {
012     System.out.println("**start**");
        //
        //Définir la valeur initiale de la carte
013     stockMap = new TreeMap<String, Integer>();
014     subStockMap = new TreeMap<String, List<String>>();
015     setInitialMap();
        //
        //Traitement des données d'inventaire
016     stock("book", 1);
017     stock("book", 2);
018     sale("book", 2);
019     get("book");
020     sale("magazine", 2);
021     get("magazine");
022     getList();
        //
023     System.out.println("**end**");
024   }
      /**
      *Méthode pour définir la valeur initiale de la carte
      */
025   private static void setInitialMap() {
026     List<String> cdlist = Arrays.asList("posterA", "posterB", "posterC");
027     subStockMap.put("cd", cdlist);
028     List<String> mglist = Arrays.asList("bagA");
029     subStockMap.put("magazine", mglist);
030     stockMap.put("cd", 3);
031     stockMap.put("magazine", 3);
032     stockMap.put("posterA", 3);
033     stockMap.put("posterB", 3);
034     stockMap.put("posterC", 3);
035     stockMap.put("bagA", 3);
036   }
      /**
      *Méthode pour effectuer le processus d'achat
      */
037   private static void stock(String productName, int quantity) {
038     if (funcOption) {
039       stockObject = new FunctionStock(productName, quantity, "add");
040     } else {
041       stockObject = new Stock(productName, quantity, "add");
042     }
043     setMap();
044     int result = stockObject.execute();
045     if (0 > result) System.exit(result);
046   }
      /**
      *Méthode de traitement des ventes
      */
047   private static void sale(String productName, int quantity) {
048     if (funcOption) {
049       stockObject = new FunctionStock(productName, -quantity, "add");
050     } else {
051       stockObject = new Stock(productName, -quantity, "add");
052     }
053     setMap();
054     int result = stockObject.execute();
055     if (0 > result) System.exit(result);
056   }
      /**
      *Méthode de sortie de la quantité en stock du produit spécifié
      */
057   private static void get(String productName) {
058     if (funcOption) {
059       stockObject = new FunctionStock(productName, "get");
060     } else {
061       stockObject = new Stock(productName, "get");
062     }
063     setMap();
064     int result = stockObject.execute();
065     if (0 > result) System.exit(result);
066   }
      /**
      *Méthode de sortie de la liste d'inventaire
      */
067   private static void getList() {
068     if (funcOption) {
069       stockObject = new FunctionStock("getlist");
070     } else {
071       stockObject = new Stock("getlist");
072     }
073     setMap();
074     int result = stockObject.execute();
075     if (0 > result) System.exit(result);
076   }
      /**
      *Méthode pour définir la carte en objet de stock
      */
077   private static void setMap() {
078     stockObject.setStockMap(stockMap);
079     stockObject.setSubStockMap(subStockMap);
080   }
081 }
[Description du code lié au stock de fonction] Pour 028 à 042, productName, quantité et type sont définis entre 013 et 014 dans le constructeur. type représente le type de traitement et add, delname, get et getlist peuvent être spécifiés. ・ Exécuter la méthode Les données de stock spécifiées sont sorties pour confirmation à 044. Vérification des données de stock spécifiées de 045 à 048. Vérifiez les données d'inventaire avec la méthode getDataCheckFunction (). Get (), obtenez le résultat sous forme de chaîne, convertissez-le en facultatif avec Optional.ofNullable, faites un jugement null facultatif avec ifPresent et affichez une erreur avec outputErrorMessage s'il y a une erreur. Je vais. En 052, le traitement des données d'inventaire est exécuté par la méthode executeStock (). Execute () (interface de fonction auto-fabriquée IStockExecuter) et le résultat du traitement est stocké dans le type générique facultatif: Integer. En 053, le message d'erreur dans le traitement des données d'inventaire est généré par la méthode getErrorKeyFunction (). Apply (result) et sorti par outputErrorMessage. -GetDataCheckFunction, méthode 066 à 078 définit l'implémentation d'une interface fonctionnelle (type générique fournisseur: chaîne) qui vérifie les données d'inventaire (contrôle nul, etc.). ・ Méthode ExecuteStock 079 à 096 définissent une implémentation d'interface fonctionnelle (de IStockExecuter) qui traite les données d'inventaire (types de traitement: add, delname, get, getlist). Il appelle updateStock (). Get (), deleteStockName (). Get (), getStock (). Get (), outputStockList (getStockList (). Get ()), outputSubStockList (). ・ Méthode UpdateStock 097 à 126 définissent la mise en œuvre d'une interface fonctionnelle (type générique fournisseur facultatif) qui met à jour le nombre de stocks. La quantité en stock est mise à jour à l'aide de la méthode addToStockMap (). Apply (・ ・). ・ Méthode AddToStockMap Définit l'implémentation d'une interface de type de fonction (type générique BiFunction: String, Integer, Optional) qui met à jour spécifiquement le nombre de stocks de 127 à 138. La méthode de calcul de Map est utilisée pour augmenter ou diminuer le nombre de stocks. -DeleteStockName méthode 139 à 147 définissent la mise en œuvre d'une interface fonctionnelle (type générique fournisseur: facultatif) qui supprime les articles à stocker (données de stock). La méthode de suppression de Map est utilisée pour supprimer les articles en stock. ・ Méthode GetStock 148 à 154 définissent la mise en œuvre d'une interface fonctionnelle (type générique fournisseur: optionnel) qui acquiert le nombre de stocks d'un article spécifique. La quantité en stock est acquise à l'aide de la méthode getOrDefault de Map. ・ Méthode GetStockList Définit l'implémentation d'une interface fonctionnelle (type générique fournisseur: chaîne) qui récupère la liste des quantités en stock de 155 à 166. La liste d'inventaire est générée à l'aide de la méthode forEach de Map. -GetErrorKeyFunction, méthode 167 à 175 définissent l'implémentation d'une interface de type de fonction (Type générique de fonction: facultatif, chaîne) qui vérifie le résultat du traitement d'inventaire. La méthode de mappage facultative (errindex) définit la génération de message en cas d'erreur. -OutputSubStockList méthode Une liste d'accessoires est sortie de 191 à 204. Les informations de sortie d'accessoire pour un élément spécifique sont générées par la méthode de collecte après la conversion de liste en flux. -OutputErrorMessage, méthode Les messages d'erreur sont émis pour messageKey de 205 à 220. Après avoir converti messageKey en facultatif, un message d'erreur est généré par la méthode map.
FunctionStock.java
001 package stocksample;
002 import java.util.Arrays;
003 import java.util.List;
004 import java.util.Map;
005 import java.util.Optional;
006 import java.util.function.BiFunction;
007 import java.util.function.Function;
008 import java.util.function.Supplier;
    /**
     *Classe de gestion des stocks
     */
009 public final class FunctionStock implements IStock {
010   private String productName;
011   private int quantity;
012   private Map<String, Integer> stockMap;
013   private Map<String, List<String>> subStockMap;
014   private String type;
015   private String errorKey;
016   private String errorProductName;
017   private final List<String> typeList = Arrays.asList("add", "delname", "get");
018   private final List<String> errorKeyList = Arrays.asList("zerostock,subzerostock", "noname", "noname");
019   private final List<String> errorMessageKeyList= Arrays.asList(
020   "nullname", "noname", "number", "zerostock", "subzerostock","keyerror");
021   private final List<String> errorMessageList= Arrays.asList(
022   "★ Le nom du produit n'est pas spécifié.",
023   "★ Le nom du produit spécifié dans la liste d'inventaire n'existe pas.",
024   "★ La quantité n'est pas spécifiée.",
025   "★ La quantité de stock sera inférieure à zéro.<%p1%> <%p2%>Pièces",
026   "★ La quantité d'accessoires en stock sera inférieure à zéro.<%p1%> <%p2%>Pièces",
027   "★ La clé est anormale.");
      /**
      *constructeur
      */
028   public FunctionStock(String productName, int quantity, String type) {
029     this.productName = productName;
030     this.quantity = quantity;
031     this.type = type;
032   };
      /**
      *constructeur
      */
033   public FunctionStock(String productName, String type) {
034     this.productName = productName;
035     this.quantity = 0;
036     this.type = type;
037   };
      /**
      *constructeur
      */
038   public FunctionStock(String type) {
039     this.productName = "";
040     this.quantity = 0;
041     this.type = type;
042   };
      /**
      *Méthode de traitement des données d'inventaire
      *☆ Utilisez les méthodes facultatives des méthodes Nullable, ifPresent et orElse
      *☆ Utilisez la méthode apply de l'interface Function
      */
043   public int execute() {
        //Sortie des données d'inventaire
044     outputData();
        //Vérification des données d'inventaire
045     Optional.ofNullable(getDataCheckFunction().get()).ifPresent(ekey -> {
046       outputErrorMessage(ekey);
047       errorKey = ekey;
048     });
049     if (null != errorKey) {
050       return -1;
051     }
        //Traitement des données d'inventaire
052     Optional<Integer> result = executeStock().execute();
        //Sortie d'erreur
053     outputErrorMessage(getErrorKeyFunction().apply(result));
054     return result.orElse(-1);
055   }
      /**
      *Méthode de sortie des données d'inventaire
      */
056   private void outputData() {
057     StringBuilder sb = new StringBuilder();
058     sb.append("Données en cours:");
059     sb.append(productName);
060     sb.append(",");
061     sb.append(quantity);
062     sb.append(",");
063     sb.append(type);
064     System.out.println(sb.toString());
065   }
      /**
      *Méthode de vérification des données d'inventaire
      *☆ Utilise l'interface fonctionnelle Fournisseur
      */
066   private Supplier<String> getDataCheckFunction() {
067     return () -> {
068       if (null == productName || (!"getlist".equals(type) && "".equals(productName))) {
069         return "nullname";
070       }
071       if ("add".equals(type)) {
072         if (0 == quantity) {
073           return "number";
074         }
075       }
076       return null;
077     };
078   }
      /**
      *Méthode de traitement des données d'inventaire
      *☆ Utilisez l'interface de type de fonction personnalisée IStockExecuter
      *☆ Utilisez la méthode vide facultative
      */
079   private IStockExecuter executeStock() {
080     return () -> {
081       Optional<Integer> result = Optional.empty();
082       if ("add".equals(type)) {
083         result = updateStock().get();
084       } else if ("delname".equals(type)) {
085         result = deleteStockName().get();
086       } else if ("get".equals(type)) {
087         result = getStock().get();
088       } else if ("getlist".equals(type)) {
089         outputStockList(getStockList().get());
090         outputSubStockList();
091       } else {
092         errorKey = "keyerror";
093       }
094       return result;
095     };
096   }
      /**
      *Méthode de mise à jour de la quantité de stock(Peut être augmenté ou diminué)
      *☆ Utilise l'interface fonctionnelle Fournisseur
      *☆ Utilisez la méthode apply de l'interface fonctionnelle BiFunction
      *☆ Utilisation facultative de la méthode
      *☆ Utilisez la liste pour chaque méthode
      *☆ Utilisez la méthode getOrDefault de Map
      */
097   private Supplier<Optional<Integer>> updateStock() {
098     return  () -> {
099       if (0 > addToStockMap().apply(productName, quantity).get()) {
100         addToStockMap().apply(productName, -quantity);
101         return Optional.of(-1);
102       }
103       if (0 > quantity) {
104         List<String> slist = subStockMap.get(productName);
105         if (null != slist) {
106           slist.forEach(v  -> {
107             if (null != errorProductName) return;
108             int substock = stockMap.getOrDefault(v, -1);
109             if (-1 == substock || 0 > (substock + quantity)) {
110               errorProductName = v;
111             }
112           });
113           if (null == errorProductName) {
114             slist.forEach(v  -> {
115               addToStockMap().apply(v, quantity);
116             });
117           }
118         }
119         if (null != errorProductName) {
120           addToStockMap().apply(productName, -quantity);
121           return Optional.of(-2);
122         }
123       }
124       return Optional.of(0);
125     };
126   }
      /**
      *Méthode de mise à jour de la valeur en stock du produit
      *☆ Utilise l'interface fonctionnelle BiFunction
      *☆ Utilisez la méthode de calcul de Map
      *☆ Utilisation facultative de la méthode
      */
127   private BiFunction<String, Integer, Optional<Integer>> addToStockMap() {
128     return (pname, qty) -> {
129       int addedValue = stockMap.compute(pname, (k, v) -> {
130         if (null == v) v = 0;
131         return v + qty;
132       });
133       if (0 > addedValue) {
134         return Optional.of(-1);
135       }
136       return Optional.of(addedValue);
137     };
138   }
      /**
      *Méthode de suppression des données d'inventaire des marchandises
      *☆ Utilise l'interface fonctionnelle Fournisseur
      *☆ Utilisez facultatif deNullable, isPresent et des méthodes
      */
139   private Supplier<Optional<Integer>> deleteStockName() {
140     return () -> {
141       int result = -1;
142       if (Optional.ofNullable(stockMap.remove(productName)).isPresent()) {
143         result = 0;
144       }
145       return Optional.of(result);
146     };
147   }
      /**
      *Méthode pour obtenir le nombre de stock
      *☆ Utilise l'interface fonctionnelle Fournisseur
      *☆ Utilisez la méthode getOrDefault de Map
      *☆ Utilisation facultative de la méthode
      */
148   private Supplier<Optional<Integer>> getStock() {
149     return () -> {
150       int result = stockMap.getOrDefault(productName, -1);
151       outputNumberStock(result);
152       return Optional.of(result);
153     };
154   }
      /**
      *Méthode pour générer la liste d'inventaire
      *☆ Utilise l'interface fonctionnelle Fournisseur
      *☆ Utilisez la méthode Map forEach
      */
155   private Supplier<String> getStockList() {
156     return () -> {
157       StringBuilder sb = new StringBuilder();
158       stockMap.forEach((k, v) -> {
159         sb.append(k);
160         sb.append(":");
161         sb.append(v);
162         sb.append("\n");
163       });
164       return sb.toString().substring(0, sb.toString().length()-1);
165     };
166   }
      /**
      *Méthode de vérification du résultat du traitement des stocks
      *☆ Interface de type de fonction Utiliser la fonction
      *☆ Utilisez les méthodes optionnelles map et orElse
      */
167   private Function<Optional<Integer>, String> getErrorKeyFunction() {
168     return errindex -> {
169       Optional<String> opkey = errindex.map(eindex -> {
170         if (0 <= eindex) return "";
171         return errorKeyList.get(typeList.indexOf(type)).split(",")[Math.abs(eindex)-1];
172       });
173       return opkey.orElse("");
174     };
175   }
      /**
      *Méthode de sortie du nombre de stocks
      */
176   private void outputNumberStock(int result) {
177     if (-1 < result) {
178       StringBuilder sb = new StringBuilder();
179       sb.append("☆ Quantité de stock du nom de stock spécifié:");
180       sb.append(productName);
181       sb.append(" ");
182       sb.append(result);
183       sb.append("Pièces");
184       System.out.println(sb.toString());
185     }
186   }
      /**
      *Méthode de sortie de la liste d'inventaire
      */
187   private void outputStockList(String list) {
188     System.out.println("☆ Liste d'inventaire");
189     System.out.println(list);
190   }
      /**
      *Méthode de sortie de la liste d'accessoires
      *☆ Utilisez les méthodes Map forEach et getOrDefault
      *☆ Utilisez la méthode de flux de List
      *☆ Utilisez la méthode de collecte de Stream
      */
191   private void outputSubStockList() {
192     System.out.println("☆ Liste d'accessoires");
193     stockMap.forEach((k, v) -> {
194       List<String> list = subStockMap.getOrDefault(k, null);
195       if (null != list) {
196         StringBuilder sb = list.stream().collect(StringBuilder::new, (ssb, adname) -> {
197           ssb.append(adname);
198           ssb.append(", ");
199         }, (ba, bb) -> {ba.append(bb);});
200         String str = k + " : " + sb.toString();
201         System.out.println(str.substring(0, str.length()-2));
202       }
203     });
204   }
      /**
      *Méthode de sortie du message d'erreur
      *☆ Utilisez facultatif des méthodes Nullable et map
      */
205   private void outputErrorMessage(String messageKey) {
206     if ("".equals(messageKey)) return;
207     Optional<String> mes = Optional.ofNullable(messageKey).map(m -> {
208       String messtr = errorMessageList.get(errorMessageKeyList.indexOf(m));
209       if (-1 < messtr.indexOf("<%p")) {
210         String pname = productName;
211         if (null != errorProductName) {
212           pname = errorProductName;
213         }
214         messtr = messtr.replace("<%p1%>", pname).replace("<%p2%>", String.valueOf(stockMap.get(pname)));
215       }
216       return messtr;
217     });
218     System.out.println(mes.get());
219     System.out.println("★ Le traitement a été annulé.");
220   }
221   public void setStockMap(Map<String, Integer> stockMap) {
222     this.stockMap = stockMap;
223   }
224   public void setSubStockMap(Map<String, List<String>> subStockMap) {
225     this.subStockMap = subStockMap;
226   }
227 }
IStock.java
001 package stocksample;
002 import java.util.List;
003 import java.util.Map;
004 public interface IStock {
005   public int execute();
006   public void setStockMap(Map<String, Integer> stockMap);
007   public void setSubStockMap(Map<String, List<String>> subStockMap);
008 }
IStockExecuter.java
001 package stocksample;
002 import java.util.Optional;
003 @FunctionalInterface
004 public interface IStockExecuter {
005   public abstract Optional<Integer> execute();
006 }
Ce qui suit est une brève explication des termes liés à la programmation de fonctions qui apparaissent souvent sur le net. Comme il est difficile de comprendre la monade, j'ai ajouté une explication supplémentaire.
[Explication supplémentaire de Monad] Écrivons une explication supplémentaire sur Monad du point de vue du programme. "Monad" est un terme général pour plusieurs types de monades (instances de monades), et est une classe de types en Haskell (langage fonctionnel). Les monades standard sont: Peut-être monade, Monade de liste, Monade d'identité, Soit monade, Monade d'État, Monade IO, Monade d'écrivain, Monade de lecteur, ... (En Haskell, on l'appelle aussi type Maybe, type List, ...) Pour devenir une monade, vous devez remplir les trois conditions suivantes. Si cette condition est remplie, elle peut être appelée une monade. (1) Recevez un argument de type. (2) Il peut être actionné par return et bind (>> = opérateur). return est une fonction pour mettre une valeur dans la monade. bind est un opérateur pour passer la valeur de la monade à la fonction, et il est décrit comme monade >> fonction et la valeur de retour est placée dans la monade. (3) Satisfaire aux règles établies de la Monade. Si vous remplissez les conditions, ce sera une monade, vous pouvez donc créer la vôtre en suivant les étapes ci-dessous. (1) Définissez le type pour être une monade. (2) Définissez une instance de Monad. (Inclut les implémentations de retour et de liaison.) Le but principal de cette monade est Valeur Monade >> = Fonction de type Monade 1 >> = Fonction de type Monade 2 >> = Fonction de type Monade 3 ---> Obtenir la valeur de monade Est de pouvoir exécuter des fonctions en continu comme. Dans Monad, il est également possible d'ajouter des traitements lors de la combinaison. Valeur de la monade: valeur de la monade. Fonction de type monade: fonction qui reçoit une valeur monade et renvoie le résultat du traitement sous forme de valeur monade Plus précisément, les monades peuvent être utilisées comme suit. (Ce n'est qu'un exemple. La langue est Haskell.)
*> let addOne x = Just (x + 1) --Définition de la fonction addOne (signifie simplement que c'est la valeur de Maybe monad) *> let addTwo x = Just (x + 2) --Définition de la fonction addTwo *> return 2 >> = addOne >> = addTwo --Peut-être Mettre 2 dans la monade et exécuter les fonctions addOne et addTwo
Résultat: seulement 5 Voici un exemple d'utilisation de la monade Maybe pour combiner deux fonctions, où >> = est l'opérateur de combinaison. Le retour final 2 >> = addOne >> = addTwo peut également être spécifié dans le format suivant. do return 2 addOne addTwo Je pense que les étapes suivantes sont bonnes pour mieux comprendre Monad.
Au fait, je n'ai jamais utilisé Haskell.
 Définition de la monade 
class Monad m where
 return :: a -> m a
 (>>=) :: m a -> (a -> m b) -> m b
 (>>) :: m a -> m b -> m b
 x >> y = x >>= \_ -> y
 fail :: String -> m a
 fail msg = error msg
c'est tout. Merci d'avoir lu jusqu'au bout.
Recommended Posts