~ I tried to learn functional programming in Java now ~

One year has passed since the posting, so I have not changed the essence of the content and reviewed and updated it focusing on adding explanations of the presentation source.

Introduction

It has been more than 4 years since Java 8 was released in March 2014, and it has been used by many people including Java 9 and Java 10 and later. Functional programming was a hot topic when Java 8 was released. Nowadays, the term functional programming is generally recognized and pervasive among system developers. However, it seems that the actual penetration is still a long way off, as the current situation is that we do not frequently see the system development results of functional programming. And, like Java 8, existing languages are also being enhanced by incorporating a functional programming style. I can't predict what the flow will be like in the future, but I personally speculate that using functional programming styles in existing languages will become the mainstream. I don't think that a pure functional language will be a specialized usage, and Scala will not replace the Java language in Java. Functional programming seems to be in such a situation, but it is expected that it will spread in some way in the future, so I did a general survey and tried to practice simple programming in the Java 10 environment. .. In addition, I decided to post the learning results as a memorandum, thinking that it might be helpful for those who are interested. Please note that the content may have a subjective part or a program style that includes personal orientation. For words and phrases specific to functional programming, please refer to the brief explanations within the range understood at the end. (* Words with numbers)

Java 8 new features added

Java 8 adds the following new features for functional programming:

Before going into these introductions, I would like to try to explain briefly what functional programming is in the first place.

What is functional programming?

In a narrow sense, functional programming is "a method of constructing a program with functions that have referential transparency (* 9) and no side effects (* 8)", and this definition is based on functional languages. However, it is now possible to implement functional programming styles even in existing languages, and here we have decided to take a broad view of functional programming and listed three features.

  1. Simplify programming by taking advantage of the ability to pass a function (such as a lambda expression) as a function argument.
  2. Simplify programming and improve quality by utilizing various methods (functions) and grammar for functional programming provided by languages and APIs.
  3. Create and utilize referentially transparent (* 9) side-effect-free functions, combine those functions to develop most of the system, and aim for a safe and high-quality system. It is generally said that if the problem target can be expressed mathematically, almost all can be developed by functions.

I think that creating a program with the above method and purpose can be recognized as functional programming from a broad perspective. In addition, functional programming is not linked to development methods such as requirements analysis design process like object-oriented programming, but is intended for the program implementation process itself. In order to perform functional programming, there are those that support at the language level such as Haskell and Scala, and those that add an API for functional programming to an existing language such as Java 8. It is said that Java 8 can cover the range from 1 to 2. Haskell and Scala can cover the range from 1 to 3. A programming language that can almost avoid side effects is also called a purely functional programming language, and from this point of view, Scala's 3. is considered to be a quasi-three rather than a pure 3. However, it goes without saying that it depends on the project whether it is necessary to configure the system with "functions that almost avoid side effects". Functional programming takes a method of creating a relatively small function and applying or combining it to develop a system, which is based on the declarative programming (* 3) format. Therefore, in order to implement level 3, it is necessary to fully understand the concept of functional languages and functional programming rather than the conventional object-oriented programming method. In addition, it is necessary to fully understand in advance how well the target problem matches functional programming, such as the system to be linked, the platform middleware, the platform framework, the related accumulated assets, and the development / management / operation environment. there is. The following items are generally cited as the advantages of functional programming.

There seems to be some case-by-case situations for the 4th and 5th items, but it seems certain that the amount of programming will be reduced for the applicable parts. On the other hand, the disadvantage is that if the development system can be expressed mathematically, it is theoretically possible to develop with pure functional programming, but in reality there are few such environments, especially CRUD processing in business systems. It is said that there is not much merit in the main application. Java8 introduces new mechanisms such as functional interfaces, lambda expressions, Streams, and Optional to support functional programming. This makes it possible to write some of the traditional imperative programming in a functional programming (declarative programming) style. And when doing functional programming in Java 8 or later,

  1. Use new APIs to the extent possible based on object-oriented programming. (Mainly reduction of coding amount)
  2. Go a little further and implement methods by creating and utilizing referentially transparent (preferably side-effect-free) functions to the extent possible. (Reduced coding amount and improved quality)

I think there are two cases, but personally, I think it is better to take steps (1) → (2) to learn. One thing to keep in mind is that functional programming only works on Java 8 and above. In order to be able to operate with older Java versions such as Java 6 and Java 7, it is necessary to develop with imperative programming as before, not with functional programming.

Overview of new Java 8 features

The main new and updated methods related to functional programming in Java 8 are as follows. You can use these methods to take advantage of the basic functionality of functional programming. I think we can express that functional programming has been incorporated into object-oriented languages. You can easily find out more about each of these methods online or in books. Here, we will give an overview of each method and a sample of typical methods.

-Functional interface Lambda expressions and method references (* 7) can be used by using the functional interface. You can make your own, but there are several standard functional interfaces available.

(): Main method name, argument type → return type The following is a simple definition and usage sample of a self-made functional interface.

[Interface Example related code description] -A program that specifies a hobby name and outputs "my hobby: hobby name". -The functional interface IHobby with the abstract method getHobby (return value: character string) is defined in 009 to 012. -In 003 to 005, the processing implementation of IHobby is defined using a lambda expression. -The getHobby method of IHobby is executed in 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 }

Below is a simple sample using the Function standard functional interface.

[LambdaParameter related code description] -A program that multiplies the specified value by 5 and outputs it. -The method getFiveTimes (return value: Integer type) that multiplies the value by 5 is defined in 016 to 018. -The execute method is executed from 007 to 009 to get the 5 times value. -The functional interface receives the lambda expression of the second argument defined in 012 from 007 to 009. -The apply method of the functional interface is executed in 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("result:" + res);
011   }

      /**
      *Argument function interface(Lambda expression)Method to execute
      */
012   public static <R> R execute(Integer value, Function<Integer, R> fnc) {
013     R rtnval = fnc.apply(value);
014     return rtnval;
015   }

      /**
      *Method that executes specific processing(Multiply the value by 5)
      */
016   public static Integer getFiveTimes(Integer value) {
017     return value * 5;
018   }

019 }

・ Map New methods have also been added to the existing Map class. The main methods added are forEach, replaceAll, computeIfAbsent, computeIfPresent, compute and merge.

Below is a simple sample. (k: Map key v: Map value p: Argument value key: Specified key)

[Map related code description] The type of map is String, String. ・ ForEach (001): Outputs all elements of the map in the [Key: Value] format. ・ ReplaceAll (002 ~): Replace the values of all map elements with null → [key] and non-null → [key + first 2 characters of value]. ・ ComputeIfAbsent (008 ~): a. If the key exists in the map key The value is not updated and the value is the return value. b. If the key exists and the value is null The value is updated to [key + "-addition"] and that value is the return value. c. If it does not exist in the key Add a value with key and value [key + "-addition"] to map, and that value will be the return value. ・ ComputeIfPresent (011 ~): a. If the key exists in the map key The value is updated to [key + value + "-addition"] and that value is the return value. b. If the key exists and the value is null The value is not updated and the return value is null. c. If it does not exist in the key The value is not added or updated, and null is the return value. ・ Compute (014 ~): a. If the key exists in the map key The value is updated to [key + value + "-addition"] and that value is the return value. b. If the key exists and the value is null The value is updated to [key + value (null) + "-addition"], and that value is the return value. c. If it does not exist in the key Add an element (key, [key + value (null) + "-addition"]) to the map and that value will be the return value. ・ Merge (017 ~): a. If the key exists in the map key The value is updated to [value + "-add"] and that value is the return value. b. If the key exists and the value is null The value is updated to ["-add"](ignoring null values) and that value is the return value. c. If it does not exist in the key Add the element (key, ["-add"]) (ignore the null value) to the map, and that value will be the return value.

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);

・ List New methods have also been added to the existing List class. The main methods added are forEach, removeIf, replaceAll, stream, parallelStream.

Below is a simple sample. (v: list value value: external value)

[List related code description] The type of list is String. ・ ForEach (001): Outputs values for all elements of list. ・ RemoveIf (002 ~): There is a specified value in list: The corresponding element is deleted and true is returned. b. Specified value is null: The corresponding element is not deleted and false is returned. c. No specified value: The element is not deleted and false is returned. ・ ReplaceAll (008 ~): Non-null value for all elements: Updated to the first two characters of the value. b. Null value for all elements: The value is not updated. ・ Sort (014): a. Sorts all elements in natural order and null maximum value. ・ Stream (015): a. Get the Stream of list. ・ ParallelStream (016): a. Get a parallel Stream of list.

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();

・ Stream API It is an API for handling arrays and collections, and can be used for data processing such as aggregation of values. Stream methods are classified into two types, one for intermediate processing and the other for termination processing, depending on the operation content. Executes the termination processing method via 0 or more intermediate processing methods. A primitive Stream API is also provided. Method for intermediate processing asDoubleStream、asLongStream、boxed、distinct、filter、flatMap、flatMapToDouble、flatMapToInt、flatMapToLong、limit、map、mapToDouble、mapToInt、mapToLong、mapToObj、onClose、parallel、peek、sequential、skip、sorted、unordered The following is a brief description of typical methods.

Below is a simple sample. (stream: Stream instance v: stream value list: external List numlimit: external value)

[Stream related code (intermediate processing) description] The type of stream is Integer. ・ Filter (001 ~): a. Stream Extracts multiples of 3 for all elements and returns with Stream . ・ Map (004): The string file name is stored in list, and it is converted from list to stream. b. After that, after converting to Stream , this value is returned. ・ FlatMap (005 ~): Processing content: For each element of list, a copy with "abc" at the beginning is created and returned by the number. 1 half-width character + 1 digit number is stored in list, and an array of ["abc" + value] is created for the number of numbers. b. Then convert this array to Stream type. Furthermore, this value is converted to List . ・ Distinct (010): a. Stream All elements are returned with the same type, eliminating duplicate elements. ・ Sorted (011): The total number of elements in the stream is sorted in ascending order and returned in the same type. ・ Limit (012): The total number of elements in the stream is limited to numlimit and returned with the same type.

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);

Termination method allMatch、anyMatch、average、collect、count、findAny、findFirst、forEach、forEachOrdered、iterator、max、min、noneMatch、reduce、spliterator、sum、summaryStatistics、toArray Below is a brief description of typical methods.

Below is a simple sample. (stream: Stream instance v: stream value array: external array ac: accumulator)

[Stream related code (termination processing) description] ・ AllMatch (001 ~): The stream is checked under the all match condition (0 10). Returns true if all elements match. ・ AnyMatch (007 ~): a. The stream is checked with any condition (= 5). : If any one or more matches, true is returned. ・ Collect (010 ~): Example 1: Converting an array to a stream and using the toList method of the Collectors class to return a List type. The Collectors class has multiple methods for converting from stream to another class. b. Example 2: Converting a list to a stream, storing the stream value in a StringBuilder and returning the isomorphism. (The actual meaning of the third argument is not in this case. It usually has a meaning in parallel processing.) The argument description of the collect method is as follows.

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:(Multiple types exist)
010   //Example 1:Arrays.stream(array).collect(Collectors.toList());
011   //Example 2:list.stream().collect(StringBuilder::new, (b, v) -> b.append(v), (b1, b2) -> b1.append(b2));
      //1st argument:Result storage object generation, 2nd argument:Store stream value 3rd argument:Object join
    count:
012   list.stream().count();
    reduce:(Multiple types exist)
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);

・ Optional Optional is a class that wraps one value. You can check for nulls and values. Optional.ofNullable, Optional.empty (), opt.isPresent (), ...

Example of functional programming in Java8

There are many samples of several lines on the net and books, but here I will present an example of a level with a certain function. However, functional programming is not something that should be done like this, but is created from the perspective of reference coding for a rudimentary understanding. The function is inventory management of products and consists of two classes, the main class and the FunctionStock class. Please note that the FunctionStock class formally presents as many API usage examples as possible, so functional programming is performed even where it is not needed. Class overview StockMain: The main class of inventory management. No functional programming is done. FunctionStock: A class that actually processes inventory. Functional programming is done with multiple methods. IStock: Inventory processing class interface. IStockExecuter: Homebrew function interface for inventory processing. Please refer to the comment of the main class for the detailed function and the comment of the method outline. The ☆ mark describes the type of functional programming used.

[StockMain related code description] Maps that store the number of stocks and accessory configurations are defined in 013 to 014. 6 items and initial stock quantity are set from 025 to 036. There are accessories for cd and magazine. Specific processing is set in 016 to 022. The book defines two purchasing processes and one selling process. magazine defines one sales process. Finally, the inventory list output process is defined. 037 to 046 are executing the purchase process by the stock method. 047 to 056 are executing sales processing with the sale method. The quantity is negative. 057 to 066 output the stock quantity of the specified product by the get method. 067 to 076 output the inventory list of products with the getList method. Purchasing process, sales process, inventory quantity output, and inventory list output are all executed by the execute method of 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;

    /**
     *Inventory management main class
     *Function: Purchasing process(stock), Sales processing(sale), Inventory check processing(get), Inventory list output processing(getList)
     * (): Method name
     *Inventory data processing format: Method name(Product name,quantity)
     *Accessories for each product(Multiple possible, but each quantity is limited to one)Is attached, and inventory management of its accessories is also performed.
     *Purchasing process: Increase the number of specified products in stock by the number of purchases. If the designated product is not registered, the product is also registered.
     *Sales processing: Reduce the number of specified products in stock by the number sold. However, if there is no stock including accessories, processing will be stopped.
     *Inventory check processing: Outputs the inventory quantity of the specified product.
     *Inventory list output processing: Outputs the number of stocks of all products. The accessory list is also output.
     *Handling of errors: When an error occurs, an error message is output and the subsequent processing is stopped.
     */
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; //JDK8 version
      //private static final boolean funcOption = false; //JDK6,JDK7 version

011   public static void main(String[] args) {
012     System.out.println("**start**");
        //
        //Set initial map value
013     stockMap = new TreeMap<String, Integer>();
014     subStockMap = new TreeMap<String, List<String>>();
015     setInitialMap();
        //
        //Inventory data processing
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   }

      /**
      *Method to set initial map value
      */
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   }

      /**
      *Method to perform purchasing process
      */
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   }

      /**
      *Method to process sales
      */
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   }

      /**
      *Method to output the stock quantity of the specified product
      */
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   }

      /**
      *Method to output inventory list
      */
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   }

      /**
      *Method to set map to stock object
      */
077   private static void setMap() {
078     stockObject.setStockMap(stockMap);
079     stockObject.setSubStockMap(subStockMap);
080   }
081 }

[Function Stock related code description] In 028 to 042, productName, quantity, and type are set in 013 to 014 in the constructor. type represents the type of processing, and add, delname, get, and getlist can be specified. ・ Execute method The specified inventory data is output for confirmation at 044. The specified inventory data is checked from 045 to 048. Check the inventory data with getDataCheckFunction (). Get () method, get the result String, convert it to Optional with Optional.ofNullable, make an optional null judgment with ifPresent, and display an error with outputErrorMessage if there is an error. I will. In 052, inventory data processing is executed by executeStock (). execute () method (IStockExecuter self-made function interface), and the processing result is stored in Optional generic type: Integer. In 053, error message generation in inventory data processing is performed by getErrorKeyFunction (). Apply (result) method, and outputErrorMessage is output. -GetDataCheckFunction method 066 to 078 defines the implementation of a functional interface (Supplier generic type: String) that checks inventory data (null check, etc.). ・ ExecuteStock method 079 to 096 define a functional interface (of IStockExecuter) implementation that processes inventory data (processing types: add, delname, get, getlist). It calls updateStock (). Get (), deleteStockName (). Get (), getStock (). Get (), outputStockList (getStockList (). Get ()), outputSubStockList (). ・ UpdateStock method 097 to 126 define the implementation of a functional interface (Supplier generic type Optional) that updates the inventory quantity. The inventory quantity is updated using the addToStockMap (). Apply (・ ・) method. -AddToStockMap method Defines the implementation of a functional interface (BiFunction generic type: String, Integer, Optional) that specifically updates the stock quantity in 127 to 138. The compute method of Map is used to increase or decrease the number of stocks. -DeleteStockName method In 139 to 147, the implementation of the functional interface (Supplier generic type: Optional) that deletes the inventory target item (inventory data) is defined. The remove method of Map is used to delete the item in stock. ・ GetStock method 148 to 154 define the implementation of a functional interface (Supplier generic type: Optional) that acquires the inventory quantity of a specific item. The stock quantity is acquired using the getOrDefault method of Map. -GetStockList method Defines the implementation of a functional interface (Supplier generic type: String) that retrieves the stock quantity list in 155 to 166. The inventory list is generated using Map's forEach method. -GetErrorKeyFunction method 167 to 175 define the implementation of a functional interface (Function generic type: Optional, String) that checks the inventory processing result. Optional (errindex) map method defines message generation in case of error. -OutputSubStockList method A list of accessories is output from 191 to 204. The accessory output information for a specific item is generated by the collect method after converting from list to stream. -OutputErrorMessage method Error message is output for messageKey from 205 to 220. After converting messageKey to Optional, an error message is generated by the map method.

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;

    /**
     *Inventory management class
     */
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   "★ The product name is not specified.",
023   "★ The product name specified in the inventory list does not exist.",
024   "★ The quantity is not specified.",
025   "★ Inventory will be less than zero.<%p1%> <%p2%>Pieces",
026   "★ The inventory of accessories will be less than zero.<%p1%> <%p2%>Pieces",
027   "★ The key is abnormal.");

      /**
      *constructor
      */
028   public FunctionStock(String productName, int quantity, String type) {
029     this.productName = productName;
030     this.quantity = quantity;
031     this.type = type;
032   };

      /**
      *constructor
      */
033   public FunctionStock(String productName, String type) {
034     this.productName = productName;
035     this.quantity = 0;
036     this.type = type;
037   };

      /**
      *constructor
      */
038   public FunctionStock(String type) {
039     this.productName = "";
040     this.quantity = 0;
041     this.type = type;
042   };

      /**
      *Methods for processing inventory data
      *☆ Use Optional ofNullable, ifPresent and orElse methods
      *☆ Use the apply method of the Function interface
      */
043   public int execute() {
        //Inventory data output
044     outputData();
        //Inventory data check
045     Optional.ofNullable(getDataCheckFunction().get()).ifPresent(ekey -> {
046       outputErrorMessage(ekey);
047       errorKey = ekey;
048     });
049     if (null != errorKey) {
050       return -1;
051     }
        //Inventory data processing
052     Optional<Integer> result = executeStock().execute();
        //Error output
053     outputErrorMessage(getErrorKeyFunction().apply(result));
054     return result.orElse(-1);
055   }

      /**
      *Method to output inventory data
      */
056   private void outputData() {
057     StringBuilder sb = new StringBuilder();
058     sb.append("Processing data:");
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   }

      /**
      *Method to check inventory data
      *☆ Uses functional interface Supplier
      */
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   }

      /**
      *Methods for processing inventory data
      *☆ Use self-made functional interface IStockExecuter
      *☆ Use Optional empty method
      */
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   }

      /**
      *Method to update inventory quantity(Can be increased or decreased)
      *☆ Uses functional interface Supplier
      *☆ Use the apply method of the functional interface BiFunction
      *☆ Use Optional of method
      *☆ Use List forEach method
      *☆ Use getOrDefault method of 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   }

      /**
      *Method to update inventory value of goods
      *☆ Uses functional interface BiFunction
      *☆ Use Map's compute method
      *☆ Use Optional of method
      */
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   }

      /**
      *Method to delete inventory data of goods
      *☆ Uses functional interface Supplier
      *☆ Use Optional ofNullable, isPresent and of methods
      */
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   }

      /**
      *Method to get the stock quantity
      *☆ Uses functional interface Supplier
      *☆ Use getOrDefault method of Map
      *☆ Use Optional of method
      */
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   }

      /**
      *Method to generate inventory list
      *☆ Uses functional interface Supplier
      *☆ Use Map forEach method
      */
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   }

      /**
      *Method to check inventory processing result
      *☆ Uses functional interface Function
      *☆ Use Optional map and orElse methods
      */
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   }

      /**
      *Method to output the number of stocks
      */
176   private void outputNumberStock(int result) {
177     if (-1 < result) {
178       StringBuilder sb = new StringBuilder();
179       sb.append("☆ Inventory quantity of the specified inventory name:");
180       sb.append(productName);
181       sb.append(" ");
182       sb.append(result);
183       sb.append("Pieces");
184       System.out.println(sb.toString());
185     }
186   }

      /**
      *Method to output inventory list
      */
187   private void outputStockList(String list) {
188     System.out.println("☆ Inventory list");
189     System.out.println(list);
190   }

      /**
      *Method to output accessory list
      *☆ Use Map forEach and getOrDefault methods
      *☆ Use List's stream method
      *☆ Use Stream's collect method
      */
191   private void outputSubStockList() {
192     System.out.println("☆ Accessory list");
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   }

      /**
      *Method to output error message
      *☆ Use Optional ofNullable and map methods
      */
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("★ Processing has been cancelled.");
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 }

Summary of functional programming implementation

Brief explanation of terms related to functional programming

Below is a brief explanation of the terms related to functional programming that often appear on the net. Since it is difficult to understand monads, I added an additional explanation.

  1. Functional interface: An interface that has only one abstract method defined and is associated with a lambda expression.
    Basically, use it with @FunctionalInterface annotation.
  2. Lambda expression: It is used to implement and describe the processing contents using the functional interface.
    The basic syntax is (argument)-> {process}, and the syntax format based on the number of argument parts is 1 argument: (str)-> {process}, 2 arguments: (str1, str2)-> {process }.
    The basic form of the processing part is {sentence 1; statement 2;… return return value;}, and since it is an expression, it is possible to return the value.
    In new methods in Java8, it is possible to pass this lambda expression as an argument, which makes simple programming possible. If you describe the processing part that has no side effects, the original functional programming can be realized in that part.
    Similar to the previous anonymous class.
  3. Declarative programming: It is a programming method that obtains output by describing the purpose, properties, constraints, etc. without describing the processing procedure (data and algorithm) for obtaining output.
    It may also refer to a general term for several languages such as pure functional languages.
    To put it simply, it is a programming method that sets what is needed and leaves it to the language, API, functions, etc. to handle it.
  4. Higher-order function: A function that can return other functions as arguments or as a result.
    In Java8, there are Map, FlatMap, Filter, Reduce, forEach, anyMatch, allMatch and so on.
  5. StreamAPI: It is an API that handles aggregates such as arrays and collections, aggregates values, and processes using data.
    This is a new feature of Java 8.
  6. Optional: Optional is a function that wraps values containing null and handles null safely.
  7. Method reference: Assigning the method itself to a variable in a functional interface is called a method reference.
    However, the number and type of arguments of the abstract method in the functional interface must match the number and type of arguments of the method you want to assign.
    The feature of method reference is that you can call a defined method without any argument, and "class name :: method name" is the basic form.
    It is also possible to treat predefined methods like lambda expressions.
    (Example)
    Example 1: list.forEach (System.out :: print);
    Example 2: Consumer c = System.out :: println;
        c.accept("abc");
    • Side effects: It means that things other than the data received as input (variable values, etc.) change (state change) and affect the subsequent results.
      Refers to assignment, increment, decrement, input / output, etc.
  8. Referential transparency (referential transparency): The property that the same input always returns the same result.
  9. Lazy evaluation: It is possible to easily incorporate a repeating structure by a calculation method in which the value is not calculated until it is needed.
    In Java8, lazy evaluation is locally supported in Stream API, and in Stream API, specific calculation is not performed in generation processing instruction / intermediate processing instruction, and termination. It is designed to be performed at the timing when the processing instruction is given. In addition, delay processing using a lambda expression is also possible.
    • Monad: A monad is a mechanism, structure, or concept that enables functions to be combined, synthesized, and executed programmatically.
      It is generally said that it is difficult to understand the details including the concept, but there are opinions that it is not difficult. It seems that there are many opinions that the manual is rather difficult.

[Additional explanation of monads] Let's write an additional explanation about monads from a program perspective. "Monad" is a general term for several types of monads (monad instances), and is a type class in Haskell (functional language). The standard monads are: Maybe monad, List monad, Identity monad, Either monad, State monad, IO monad, Writer monad, Reader monad, ... (In Haskell, it is also called Maybe type, List type, ...) To become a monad, you need to meet the following three conditions. If this condition is met, it can be called a monad. (1) Receive one type argument. (2) It can be operated by return and bind (>> = operator). return is a function to put a value in a monad. bind is an operator for passing a monad value to a function, and it is described as monad >> function and the return value is put in the monad. (3) Satisfy the established monadic rules. If you meet the conditions, it will be a monad, so you can make your own by following the steps below. (1) Define the type to be a monad. (2) Define an instance of Monad. (Includes return and bind implementations.) The main purpose of this monad is Monad value >> = Monad type function 1 >> = Monad type function 2 >> = Monad type function 3 ---> Get monad value Is to be able to execute functions continuously like. It is also possible to add some processing when combining in monads. Monad value: The value that a monad has. Monad type function: A function that receives a monad value and returns the processing result as a monad value. Specifically, monads can be used as follows. (It's just an example. The language is Haskell.)

*> let addOne x = Just (x + 1) --Definition of function addOne (Just means that it is the value of the Maybe monad) *> let addTwo x = Just (x + 2) --Definition of function addTwo *> return 2 >> = addOne >> = addTwo --Maybe Put 2 in the monad and execute the functions addOne and addTwo

Result: Just 5 This is an example of using the Maybe monad to combine two functions, where >> = is the operator for combining. The final return 2 >> = addOne >> = addTwo can also be specified in the following format.   do return 2    addOne    addTwo I think the following steps are good for understanding monads further.

By the way, I have never used Haskell.


Monad definition
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

Finally##

that's all. Thank you for reading to the end.

Recommended Posts

~ I tried to learn functional programming in Java now ~
I tried to summarize Java 8 now
I tried to implement deep learning in Java
I tried to output multiplication table in Java
I tried to create Alexa skill in Java
I tried metaprogramming in Java
I tried to organize the cases used in programming
I tried to implement Firebase push notification in Java
# 2 [Note] I tried to calculate multiplication tables in Java.
I tried to create a Clova skill in Java
I tried to implement the Euclidean algorithm in Java
I tried to find out what changed in Java 9
Introduction to Functional Programming (Java, Javascript)
How to learn JAVA in 7 days
I tried to interact with Java
I tried using JWT in Java
I tried using Dapr in Java to facilitate microservice development
I tried to make a client of RESAS-API in Java
I tried using Elasticsearch API in Java
Understand how functional programming was introduced to Java in one shot
I tried to summarize Java lambda expressions
I tried the new era in Java
I tried setting Java beginners to use shortcut keys in eclipse
I tried to make an Android application with MVC now (Java)
I tried to make a talk application in Java using AI "A3RT"
I tried to implement polymorphic related in Nogizaka.
I want to send an email in Java.
I tried to organize the session in Rails
java I tried to break a simple block
I wanted to make (a == 1 && a == 2 && a == 3) true in Java
rsync4j --I want to touch rsync in Java.
[Ruby basics] I tried to learn modules (Chapter 1)
I tried to build Micra mackerel in 1 hour!
I tried to develop an application in 2 languages
I tried to break a block with java (1)
Constraint programming in Java
I tried Mastodon's Toot and Streaming API in Java
I want to do something like "cls" in Java
I tried to implement TCP / IP + BIO with JAVA
[Java 11] I tried to execute Java without compiling with javac
I tried using Google Cloud Vision API in Java
[Java] I tried to solve Paiza's B rank problem
I tried to operate SQS using AWS Java SDK
I want to use ES2015 in Java too! → (´ ・ ω ・ `)
I tried to implement Stalin sort with Java Collector
[Java] I tried to implement Yahoo API product search
I tried using an extended for statement in Java
I tried passing Java Silver in 2 weeks without knowing Java
I tried to explain Effective Java 3rd edition "almost all chapters" in "easy-to-read Japanese".
Try functional type in Java! ①
I made roulette in Java.
I tried Drools (Java, InputStream)
I tried using Java REPL
Implement functional quicksort in Java
Java8 to start now ~ Optional ~
I tried to verify yum-cron
[Java] Basic terms in programming
I tried to create a java8 development environment with Chocolatey
I tried to modernize a Java EE application with OpenShift.
[JDBC] I tried to access the SQLite3 database from Java.
I tried to summarize the basics of kotlin and java