zoukankan      html  css  js  c++  java
  • Lambda Expressions

    Backgroud or Issues:

      It is unclear and complex to implement a functional interface(only contains one abstract methond)

         with an anonymous class when you want to pass the function as an argument to a method.

      Lambda Expression allows you to treat functionality as method argument, or code as data.

    Ideal Use Case for Lambda Expressions

      The following code shows that administrators in an application set some conditions to select members.

      Person class(member class)

     1 public class Person {
     2 
     3   public enum Sex {
     4     MALE, FEMALE
     5   }
     6 
     7   String name;
     8   LocalDate birthday;
     9   Sex gender;
    10   String emailAddress;
    11 
    12   Person(String nameArg, LocalDate birthdayArg,
    13          Sex genderArg, String emailArg) {
    14     name = nameArg;
    15     birthday = birthdayArg;
    16     gender = genderArg;
    17     emailAddress = emailArg;
    18   }
    19 
    20   public int getAge() {
    21     return birthday
    22         .until(IsoChronology.INSTANCE.dateNow())
    23         .getYears();
    24   }
    25 
    26   public void printPerson() {
    27     System.out.println(name + ", " + this.getAge());
    28   }
    29 
    30   public Sex getGender() {
    31     return gender;
    32   }
    33 
    34   public String getName() {
    35     return name;
    36   }
    37 
    38   public String getEmailAddress() {
    39     return emailAddress;
    40   }
    41 
    42   public LocalDate getBirthday() {
    43     return birthday;
    44   }
    45 
    46   public static int compareByAge(Person a, Person b) {
    47     return a.birthday.compareTo(b.birthday);
    48   }
    49 
    50   public static List<Person> createRoster() {
    51 
    52     List<Person> roster = new ArrayList<>();
    53     roster.add(
    54         new Person(
    55             "Fred",
    56             IsoChronology.INSTANCE.date(1980, 6, 20),
    57             Person.Sex.MALE,
    58             "fred@example.com"));
    59     roster.add(
    60         new Person(
    61             "Jane",
    62             IsoChronology.INSTANCE.date(1990, 7, 15),
    63             Person.Sex.FEMALE, "jane@example.com"));
    64     roster.add(
    65         new Person(
    66             "George",
    67             IsoChronology.INSTANCE.date(1991, 8, 13),
    68             Person.Sex.MALE, "george@example.com"));
    69     roster.add(
    70         new Person(
    71             "Bob",
    72             IsoChronology.INSTANCE.date(2000, 9, 12),
    73             Person.Sex.MALE, "bob@example.com"));
    74 
    75     return roster;
    76   }
    77 }
    View Code

       RosterTest class(main class)

      1 public class RosterTest {
      2 
      3   // functional interface of only one abstract method,
      4   // which is so simple that it has no worth in there,
      5   // you can see better usages in Approach 6 and later
      6   interface CheckPerson {
      7     boolean test(Person p);
      8   }
      9 
     10   // Approach 1: Create Methods that Search for Persons that Match One
     11   // Characteristic
     12 
     13   public static void printPersonsOlderThan(List<Person> roster, int age) {
     14     for (Person p : roster) {
     15       if (p.getAge() >= age) {
     16         p.printPerson();
     17       }
     18     }
     19   }
     20   /**
     21    * review
     22    * advantage: simplistic
     23    * disadvantage: the code is brittle, what if you want to print
     24    * members younger than a certain age ?
     25    */
     26 
     27   // Approach 2: Create More Generalized Search Methods
     28 
     29   public static void printPersonsWithinAgeRange(
     30       List<Person> roster, int low, int high) {
     31     for (Person p : roster) {
     32       if (low <= p.getAge() && p.getAge() < high) {
     33         p.printPerson();
     34       }
     35     }
     36   }
     37   /**
     38    * review
     39    * advantage: more generic
     40    * disadvantage: what if you want to print member of a specified sex,
     41    * or other specified criteria ?
     42    * suggest: do not creat another new method with different conditions,
     43    * you should separate the code in a different class.
     44    */
     45 
     46   // Approach 3: Specify Search Criteria Code in a Local Class
     47   // Approach 4: Specify Search Criteria Code in an Anonymous Class
     48   // Approach 5: Specify Search Criteria Code with a Lambda Expression
     49 
     50   public static void printPersons(
     51       List<Person> roster, CheckPerson tester) {
     52     for (Person p : roster) {
     53       if (tester.test(p)) {
     54         p.printPerson();
     55       }
     56     }
     57   }
     58 
     59   // Approach 6: Use Standard Functional Interfaces with Lambda Expressions
     60 
     61   public static void printPersonsWithPredicate(
     62       List<Person> roster, Predicate<Person> tester) {
     63     for (Person p : roster) {
     64       if (tester.test(p)) {
     65         p.printPerson();
     66       }
     67     }
     68   }
     69   /**
     70    * review
     71    * advantage: omit the functional interface through using an
     72    * existing functional interface Predicate<T> in JDK
     73    *
     74    */
     75 
     76   // Approach 7: Use Lambda Expressions Throughout Your Application
     77 
     78   public static void processPersons(
     79       List<Person> roster,
     80       Predicate<Person> tester,
     81       Consumer<Person> block) {
     82     for (Person p : roster) {
     83       if (tester.test(p)) {
     84         block.accept(p);
     85       }
     86     }
     87   }
     88 
     89   // Approach 7, second example
     90 
     91   public static void processPersonsWithFunction(
     92       List<Person> roster,
     93       Predicate<Person> tester,
     94       Function<Person, String> mapper,
     95       Consumer<String> block) {
     96     for (Person p : roster) {
     97       if (tester.test(p)) {
     98         String data = mapper.apply(p);
     99         block.accept(data);
    100       }
    101     }
    102   }
    103 
    104   // Approach 8: Use Generics More Extensively
    105 
    106   public static <X, Y> void processElements(
    107       Iterable<X> source,
    108       Predicate<X> tester,
    109       Function<X, Y> mapper,
    110       Consumer<Y> block) {
    111     for (X p : source) {
    112       if (tester.test(p)) {
    113         Y data = mapper.apply(p);
    114         block.accept(data);
    115       }
    116     }
    117   }
    118 
    119   public static void main(String... args) {
    120 
    121     List<Person> roster = Person.createRoster();
    122 
    123     for (Person p : roster) {
    124       p.printPerson();
    125     }
    126 
    127     // Approach 1: Create Methods that Search for Persons that Match One
    128     // Characteristic
    129 
    130     System.out.println("Persons older than 20:");
    131     printPersonsOlderThan(roster, 20);
    132     System.out.println();
    133 
    134     // Approach 2: Create More Generalized Search Methods
    135 
    136     System.out.println("Persons between the ages of 14 and 30:");
    137     printPersonsWithinAgeRange(roster, 14, 30);
    138     System.out.println();
    139 
    140     // Approach 3: Specify Search Criteria Code in a Local Class
    141 
    142     System.out.println("Persons who are eligible for Selective Service:");
    143 
    144     // the declaration of local class is additional than anonymous class
    145     class CheckPersonEligibleForSelectiveService implements CheckPerson {
    146       public boolean test(Person p) {
    147         return p.getGender() == Person.Sex.MALE
    148             && p.getAge() >= 18
    149             && p.getAge() <= 25;
    150       }
    151     }
    152     /**
    153      * review
    154      * advantage: less brittle
    155      * disadvantage: have additional code-a new interface and a local lass
    156      * for each search
    157      * suggest: use an anonymous class, don't need declare a new class for
    158      * each search
    159      */
    160 
    161     printPersons(
    162         roster, new CheckPersonEligibleForSelectiveService());
    163 
    164     System.out.println();
    165 
    166     // Approach 4: Specify Search Criteria Code in an Anonymous Class
    167 
    168     System.out.println("Persons who are eligible for Selective Service " +
    169         "(anonymous class):");
    170 
    171     printPersons(
    172         roster,
    173         // more brittle than local class, don't have declaration of class
    174         // but bulky because of only one method in the interface
    175         new CheckPerson() {
    176           public boolean test(Person p) {
    177             return p.getGender() == Person.Sex.MALE
    178                 && p.getAge() >= 18
    179                 && p.getAge() <= 25;
    180           }
    181         }
    182     );
    183     /**
    184      * review
    185      * advantage: reduce the code scale
    186      * disadvantage: bulky as for there is only one method in the interface
    187      * suggest: use a lambda expression
    188      */
    189 
    190     System.out.println();
    191 
    192     // Approach 5: Specify Search Criteria Code with a Lambda Expression
    193 
    194     System.out.println("Persons who are eligible for Selective Service " +
    195         "(lambda expression):");
    196 
    197     printPersons(
    198         roster,
    199         // lambda expression, omit the name of abstract method
    200         (Person p) -> p.getGender() == Person.Sex.MALE
    201             && p.getAge() >= 18
    202             && p.getAge() <= 25
    203     );
    204     /**
    205      * review
    206      * advantage: more concise
    207      * disadvantage: not the best practice
    208      * suggest: use a standard functional interface provided by java.util.function,
    209      * instead of defining a new interface
    210      */
    211 
    212     System.out.println();
    213 
    214     // Approach 6: Use Standard Functional Interfaces with Lambda
    215     // Expressions
    216 
    217     System.out.println("Persons who are eligible for Selective Service " +
    218         "(with Predicate parameter):");
    219 
    220     printPersonsWithPredicate(
    221         roster,
    222         p -> p.getGender() == Person.Sex.MALE
    223             && p.getAge() >= 18
    224             && p.getAge() <= 25
    225     );
    226 
    227     System.out.println();
    228 
    229     // Approach 7: Use Lambda Expressions Throughout Your Application
    230 
    231     System.out.println("Persons who are eligible for Selective Service " +
    232         "(with Predicate and Consumer parameters):");
    233 
    234     processPersons(
    235         roster,
    236         p -> p.getGender() == Person.Sex.MALE
    237             && p.getAge() >= 18
    238             && p.getAge() <= 25,
    239         p -> p.printPerson()
    240     );
    241 
    242     System.out.println();
    243 
    244     // Approach 7, second example
    245 
    246     System.out.println("Persons who are eligible for Selective Service " +
    247         "(with Predicate, Function, and Consumer parameters):");
    248 
    249     processPersonsWithFunction(
    250         roster,
    251         p -> p.getGender() == Person.Sex.MALE
    252             && p.getAge() >= 18
    253             && p.getAge() <= 25,
    254         p -> p.getEmailAddress(),
    255         email -> System.out.println(email)
    256     );
    257 
    258     System.out.println();
    259 
    260     // Approach 8: Use Generics More Extensively
    261 
    262     System.out.println("Persons who are eligible for Selective Service " +
    263         "(generic version):");
    264 
    265     processElements(
    266         roster,
    267         p -> p.getGender() == Person.Sex.MALE
    268             && p.getAge() >= 18
    269             && p.getAge() <= 25,
    270         p -> p.getEmailAddress(),
    271         email -> System.out.println(email)
    272     );
    273 
    274     System.out.println();
    275 
    276     // Approach 9: Use Bulk Data Operations That Accept Lambda Expressions
    277     // as Parameters
    278 
    279     System.out.println("Persons who are eligible for Selective Service " +
    280         "(with bulk data operations):");
    281 
    282     roster
    283         // Obtain a source of objects
    284         .stream()
    285         // Filter objects that match a Predicate object
    286         .filter(
    287             p -> p.getGender() == Person.Sex.MALE
    288                 && p.getAge() >= 18
    289                 && p.getAge() <= 25)
    290         // Map objects to another value as specified by a Function object
    291         .map(p -> p.getEmailAddress())
    292         // Perform an action as specified by a Consumer object
    293         .forEach(email -> System.out.println(email));
    294   }
    295 }
    View Code

    Syntax of Lambda Expressions

      A comma-separated list of formal parameters enclosed in parentheses. 

      Note:You can omit the data type of the parameters in a lambda expression.

      In addition, you can omit the parentheses if there is only one parameter.

      For example

    1 p -> p.getGender() == Person.Sex.MALE 
    2     && p.getAge() >= 18
    3     && p.getAge() <= 25
    View Code

      A -> token

      A body, which consists of a single expression or a statement block.

      For example

     1 //a body of a single expression
     2 p.getGender() == Person.Sex.MALE 
     3     && p.getAge() >= 18
     4     && p.getAge() <= 25
     5 
     6 // a statement block
     7 p -> {
     8     return p.getGender() == Person.Sex.MALE
     9         && p.getAge() >= 18
    10         && p.getAge() <= 25;
    11 }
    12 
    13 // a void method invocation
    14 email -> System.out.println(email)
    View Code

     Note that you can consider lambda expressions as anonymous methods—methods without a name.

    Accessing Local Variables of the Enclosing Scope

      Lambda expressions have access to local variables of the encolsing scope, but have no shadowing issues, 

      as they are lexically scoped, they don't inherit any names from a supertype or introduce a new level of scope.

      For example

     1 import java.util.function.Consumer;
     2 
     3 public class LambdaScopeTest {
     4 
     5     public int x = 0;
     6 
     7     class FirstLevel {
     8 
     9         public int x = 1;
    10 
    11         void methodInFirstLevel(int x) {
    12             
    13             // The following statement causes the compiler to generate
    14             // the error "local variables referenced from a lambda expression
    15             // must be final or effectively final" in statement A:
    16             //
    17             // x = 99;
    18             
    19             Consumer<Integer> myConsumer = (y) -> 
    20             {
    21                 System.out.println("x = " + x); // Statement A, 23
    22                 System.out.println("y = " + y); // 23
    23                 System.out.println("this.x = " + this.x); // 1
    24                 System.out.println("LambdaScopeTest.this.x = " +
    25                     LambdaScopeTest.this.x); // 0
    26             };
    27 
    28             myConsumer.accept(x);
    29 
    30         }
    31     }
    32 
    33     public static void main(String... args) {
    34         LambdaScopeTest st = new LambdaScopeTest();
    35         LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
    36         fl.methodInFirstLevel(23);
    37     }
    38 }
    View Code

       Note that you cann't substitute x in place of y in the following code, otherwise the compile generates a error 

      "variable x is already defined in method methodInFirstLevel(int)" because

      the lambda expression does not introduce a new level of scoping.

    1 Consumer<Integer> myConsumer = (x) -> {  
    2     // ...  
    3 } 
    View Code

       Like local and anonymous classes, a lambda expression can only access local variables

      and parameters of the enclosing block that are final or effectively final.

      The following code is error

    1 void methodInFirstLevel(int x) {
    2     x = 99;
    3     // ...
    4 }
    View Code

    Target Typing

      Now recall a lambda expression an two methods 

    1 p -> p.getGender() == Person.Sex.MALE
    2     && p.getAge() >= 18
    3     && p.getAge() <= 25
    4 
    5 public static void printPersons(List<Person> roster, CheckPerson tester)
    6 
    7 public void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester)
    View Code

      When jvm invokes the printPersons method, the parameter type is CheckerPerson, so as to the lambda expression.

      In the same way, When printPersonsWithPredicate invoked, the target type is Predicate<Person>, so as to the lambda expression.

      The data type that these methods expect is called the target type

      To determine the type of a lambda expression, the Java compiler uses the target type of the context or situation

       in which the lambda expression was found

    Target Types and Method Arguments 

      Consider the following two functional interfaces

    1 public interface Runnable {
    2     void run();
    3 }
    4 
    5 public interface Callable<V> {
    6     V call();
    7 }
    View Code

      You have overloaded the method invoke as follows

    1 void invoke(Runnable r) {
    2     r.run();
    3 }
    4 
    5 <T> T invoke(Callable<T> c) {
    6     return c.call();
    7 }
    View Code 

      Which method will be invoked in the following statement?

    1 String s = invoke(() -> "done");
    View Code

      The method invoke(Callable<T>) will be invoked because that method returns a value;

      the method invoke(Runnable) does not. In this case, the type of the lambda expression () -> "done" is Callable<T>.

    Serialization

      strongly discouraged

    reference from: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#syntax

      

  • 相关阅读:
    Flutter form 的表单 input
    FloatingActionButton 实现类似 闲鱼 App 底部导航凸起按钮
    Flutter 中的常见的按钮组件 以及自 定义按钮组件
    Drawer 侧边栏、以及侧边栏内 容布局
    AppBar 自定义顶部导航按钮 图标、颜色 以及 TabBar 定义顶部 Tab 切换 通过TabController 定义TabBar
    清空路由 路由替换 返回到根路由
    应对ubuntu linux图形界面卡住的方法
    [转] 一块赚零花钱
    [转]在树莓派上搭建LAMP服务
    ssh保持连接
  • 原文地址:https://www.cnblogs.com/yangwu-183/p/13457167.html
Copyright © 2011-2022 走看看