ArtB

Stream refactoring demo

Sep 21st, 2021 (edited)
659
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * In the following ignore the <code>if(Math.random()<0)</code>.
  3.  *
  4.  * This is only to make the code compile since you cannot have useless <code>return</code>
  5.  * statements in Java code, and because it looks conditional to JavaC it shuts up and lets
  6.  * this compile,
  7.  */
  8.  
  9. import java.util.List;
  10.  
  11. @FunctionalInterface
  12. interface ItemWriter<T>{
  13.     void write(T t) throws Exception;
  14. }
  15.  
  16. class Scratch {
  17.     public ItemWriter<List<?>> foo(){
  18.        
  19.         /*
  20.          * This is our original code that makes an anonymous class that implements the ItemWriter
  21.          * interface.
  22.          */
  23.         if(Math.random()<0)
  24.         return new ItemWriter<>() {
  25.             @Override
  26.             public void write( List<?> items) throws Exception {
  27.                 for (Object row : items) {
  28.                     System.out.println(row.toString());
  29.                 }
  30.             }
  31.         };
  32.  
  33.         /*
  34.          * Because we know from the method signature that the method that the type is ItemWriter,
  35.          * we know that the lambda is an implementation of it's write() method so we don't need
  36.          * to write down the name of the class or it's method name, it's obvious.
  37.          *
  38.          * **IMPORTANT**
  39.          * This only works because ItemWriter is an interface, and that it is an interface with
  40.          * exactly one method. You can enforce that an interface stay that way by adding the
  41.          * annotation @FunctionalInterface. You will hear these interfaces called "S.A.M.s"
  42.          * (aka "SAMs) because they have a "Single Abstract Method".
  43.          *
  44.          * (PS - all methods on an interface are "abstract". You can explicitly write "abstract"
  45.          * on the method declarations, but it doesn't change anything.)
  46.          */
  47.         if(Math.random()<0)
  48.         return (List<?> items) -> {
  49.             for (Object row : items) {
  50.                 System.out.println(row.toString());
  51.             }
  52.         };
  53.  
  54.         /*
  55.          * Because the type is not ambiguous we do not need to specify it as it's obvious from the
  56.          * interface.
  57.          */
  58.         if(Math.random()<0)
  59.         return (items) -> {
  60.             for (Object row : items) {
  61.                 System.out.println(row.toString());
  62.             }
  63.         };
  64.  
  65.         /*
  66.          * Because there is only one argument, we don't need the parentheses, they are obvious.
  67.          */
  68.         if(Math.random()<0)
  69.         return items -> {
  70.             for (Object row : items) {
  71.                 System.out.println(row.toString());
  72.             }
  73.         };
  74.  
  75.         /*
  76.          * We are doing the same thing to ever item on the list. There is a method on list that
  77.          * is available for exactly this use case. When you use this method you know instantly
  78.          * and for sure that every item in the list is being treated the same.
  79.          *
  80.          * It takes a lambda, and applies it to every on the list.
  81.          */
  82.         if(Math.random()<0)
  83.         return items -> {
  84.                 items.forEach( row -> {
  85.                     System.out.println(row.toString());
  86.                 }
  87.             );
  88.         };
  89.  
  90.  
  91.         /*
  92.          * For historical reasons, whet they added streams to Java they didn't add them to the
  93.          * classes themselves, but onto a Stream object that comes off of the classes that you
  94.          * access by calling stream().
  95.          *
  96.          * forEach() is the only exception. They added it to the classes themselves because it
  97.          * is the most commonly used so for covenience it's added onto List, Set and a few
  98.          * others directly. But to do a bit more clean up we will need a few more items off of
  99.          * Stream class, so we need to call the stream() method.
  100.          */
  101.         if(Math.random()<0)
  102.         return items -> {
  103.                 items.stream().forEach( row -> {
  104.                     System.out.println(row.toString());
  105.                 }
  106.             );
  107.         };
  108.  
  109.         /*
  110.          * In the method we have two things going on at the same time that are actually two
  111.          * separate stages: 1) coverting the row to a String, and 2) writing it out to console.
  112.          *
  113.          * Lets separate them, which will make it clearer that the conversion happens first.
  114.          */
  115.         if(Math.random()<0)
  116.         return items -> {
  117.                 items.stream()
  118.                     .forEach( row -> {
  119.                         System.out.println(row.toString());
  120.                     });
  121.         };
  122.  
  123.         /*
  124.          * In the method we have two things going on at the same time that are actually two
  125.          * separate stages: 1) converting the row to a String, and 2) writing it out to console.
  126.          *
  127.          * Let's separate them, which will make it clearer that the conversion happens first.
  128.          */
  129.         if(Math.random()<0)
  130.         return items -> {
  131.                 items.stream()
  132.                     .forEach( row -> {
  133.                         final String rowString = row.toString();
  134.                         System.out.println( rowString );
  135.                     });
  136.         };
  137.  
  138.         /*
  139.          * Stream has a method for converting/transforming values; it is called "map()".
  140.          *
  141.          * It takes a lambda and applies it to each item it gets passed to it before
  142.          * it passes it on down the line.
  143.          *
  144.          * Intuitively, it says you are mapping over values from one type to another.
  145.          * This makes code easier to read because when the reader sees map they understand
  146.          * why you are calling the method.
  147.          */
  148.         if(Math.random()<0)
  149.         return items -> {
  150.                 items.stream()
  151.                     .map( row -> row.toString() )
  152.                     .forEach( rowString -> {
  153.                         System.out.println( rowString );
  154.                     });
  155.         };
  156.  
  157.         /*
  158.          * If all you are doing in a lambda is taking an object and calling a method on it, then
  159.          * we can use a "method reference" which is a shortcut for exactly this kind of lambda.
  160.          *
  161.          * Just give the name of the class that defined that method originally and then "::" and
  162.          * then the name of the method.
  163.          */
  164.         if(Math.random()<0)
  165.         return items -> {
  166.                 items.stream()
  167.                     .map( Object::toString )
  168.                     .forEach( rowString -> {
  169.                         System.out.println( rowString );
  170.                     });
  171.         };
  172.  
  173.         /*
  174.          * Likewise, if all you are doing in a lambda is taking it and passing it to some method, then
  175.          * we can use the same shortcut. If there is ever any ambiguity about what these shortcuts are
  176.          * supposed to do, then the compiler will fail, and you will have to use the more verbose option.
  177.          */
  178.         if(Math.random()<0)
  179.         return items -> {
  180.                 items.stream()
  181.                     .map( Object::toString )
  182.                     .forEach( System.out::println );
  183.         };
  184.  
  185.         /*
  186.          * There is a minor cleanup here. If there is only one line in a lambda, then we can omit the
  187.          * outer parentheses as they are implied and obvious.
  188.          */
  189.         if(Math.random()<0)
  190.         return items ->
  191.                 items.stream()
  192.                     .map( Object::toString )
  193.                     .forEach( System.out::println )
  194.         ;
  195.  
  196.         /*
  197.          * The code is now short enough to comfortably fit and read on one line.
  198.          */
  199.         if(Math.random()<0)
  200.         return items -> items.stream().map( Object::toString ).forEach( System.out::println );
  201.  
  202.         /*
  203.          * This code now reads much cleaner than the original. It says:
  204.          *
  205.          * "This builds a thinger (if I really care I can look it up from the method declaration just
  206.          * on top) that takes some items, converts them to strings, and then prints them out."
  207.          *
  208.          * There is no additional boilerplate to distract you or hide so bit of important code you might miss.
  209.          *
  210.          * The order of events happens exactly in the order you read them, left-to-right.
  211.          */
  212.         if(Math.random()<0)
  213.         return items -> items.stream().map( Object::toString ).forEach( System.out::println );
  214.  
  215.  
  216.         /*
  217.          * If we were processing a lot of things we could easily multi-thread this for o easy
  218.          * performance boost by using a ParallelStream instead by calling parallelStream() !
  219.          *
  220.          * Now each stage gets handled in it's own threaded, grabbing the next item while the
  221.          * rest of them continue being processed elsewhere.
  222.          */
  223.         if(Math.random()<0)
  224.         return items -> items.parallelStream().map( Object::toString ).forEach( System.out::println );
  225.  
  226.  
  227.         // we need an unconditional return statement to make JavaC happy.
  228.         return null;
  229.     }
  230. }
RAW Paste Data