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 }
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 }
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
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)
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 }
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 }
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 }
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)
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 }
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 }
Which method will be invoked in the following statement?
1 String s = invoke(() -> "done");
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