在Java中传递一个代码段并不容易,不能直接传递代码段。Java是一个面向对象语言,所以必须构造一个对象,这个对象的类需要一个方法能包含所需的代码。lambda的出现有效的解决这个问题,让代码变得更加简洁。
示例:
class LengthComparator implements Comparator<String>{ public int compare(String first,String second){ return first.length() - second.length(); } } ... Arrays.sort(Strings,new LengthComparator());
Arrays.sort(strings,
(String first,String second)-> first.length() - second.length());
lambda表达式,也可称为闭包,是一个可传递的代码块,可以执行一次或多次。
一、lambda表达式的语法:
lambda表达式的语法:参数,箭头(->)以及一个表达式。
(String first,String second)
-> first.length() - second.length();
如无法放在一个表达式中,可放在{}中:
(String first,String second)-> { if(first.length() < second.length()) return -1; else if(first.length() > second.length()) return 1; else return 0; }
即使lambda表达式没有参数,仍然要提供空括号,就像无参数方法一样:
()-> {for(int i = 100; i >= 0 ; i--) System.out.println(i) ; }
如果方法只有一个参数,而且这个参数的类型可以推导得出,那么甚至可以省略小括号:
ActionListener listener = event ->
System.out.println("The times is" + new Date());
//Instead of (event) -> ... or (ActionEvent event) -> ...
无需指定lambda表达式的返回值类型,lambda表达式的返回值类型总是会由上下文推导得出,例如:
(String first,String second)-> first.length() - second.length();
可以在需要int类型结果的上下文中使用。
示例:
1 package lambda; 2 3 import javax.swing.*; 4 import java.util.Arrays; 5 import java.util.Date; 6 7 /** 8 * Created by kong on 20/11/2017. 9 */ 10 11 public class LambdaTest { 12 public static void main(String[] args) { 13 String[] planets = new String[]{"Mercury","Venus","Mars", 14 "Jupiter","Saturn","Uranus","Neptune"}; 15 System.out.println (Arrays.toString (planets)); 16 System.out.println ("Sorted in dictionary order:"); 17 Arrays.sort (planets); 18 System.out.println (Arrays.toString (planets)); 19 System.out.println ("Sorted by length:"); 20 Arrays.sort (planets,(first,second) -> first.length () - second.length ()); 21 System.out.println (Arrays.toString (planets)); 22 23 Timer t = new Timer (1000, event -> 24 System.out.println ("The time is "+ new Date ())); 25 t.start(); 26 JOptionPane.showMessageDialog (null,"Quit program?"); 27 System.exit (0); 28 } 29 }
//运行结果
[Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune]
Sorted in dictionary order:
[Jupiter, Mars, Mercury, Neptune, Saturn, Uranus, Venus]
Sorted by length:
[Mars, Venus, Saturn, Uranus, Jupiter, Mercury, Neptune]
The time is Mon Nov 20 19:58:41 CST 2017
The time is Mon Nov 20 19:58:42 CST 2017
二、函数式接口
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口称为函数式接口(functional interface)。
三、方法的引用
要用 :: 操作符分隔方法名与对象或类名。主要有三种情况:
-
- object::instanceMethod
- Class::staticMethod
- Class::instanceMethod
在前两种情况中,方法引用等价于提供方法参数的lambda表达式。如:
System.out::println 等价于 x -> System.out.println(x);
Math::pow 等价于(x,y) -> Math.pow(x,y)。
对于第三种情况下,第一个参数会成为方法的目标,如:
String::compareToIgnoreCase 等同于(x,y) -> x.compareToIgnoreCase(y)。
示例:
1 import java.util.List; 2 import java.util.ArrayList; 3 4 public class Java8Tester { 5 public static void main(String args[]){ 6 List names = new ArrayList(); 7 8 names.add("Google"); 9 names.add("Runoob"); 10 names.add("Taobao"); 11 names.add("Baidu"); 12 names.add("Sina"); 13 14 names.forEach(System.out::println); 15 } 16 }
四、构造器引用
构造器引用与方法引用类似,只不过方法名为new。如:
ArrayList<String> names = ...;
Stream<Person> stream = names.stream().map(Person::new);
List<Person> people = stream.collect(Collectors.toList());
map方法会为各个列表元素调用Person(String)构造器,如果有多个构造器,编译器会选择有一个String参数的构造器,因为上下文推导出这是在对一个字符串调用构造器。
又如:int[]::new 是一个构造器引用,它有一个参数,即数组的长度。这等价于lambda表达式 x -> new int[x] 。
五、变量作用域
在lambda表达式中捕获的变量必须实际上是最终变量(即这个变量初始之后就不会再为它赋新值);
下面做法是不合法的:
1 public static void countDowm(int start,int delay){ 2 ActionListener listener = event -> { 3 start--;//Error:Can't mutate captured variable 4 System.out.println(start); 5 }; 6 new Timer(delay,listener).start(); 7 }
在lambda表达式中声明与一个局部变量同名的参数或局部变量是不合法的。如:
1 Path first = Paths.get("/urs/bin"); 2 Comparator<String> comp = 3 (first,second) -> first.length() - second.length(); 4 //Error:Variable first already defined
六、处理lambda表达式
使用lambda表达式的重点是延迟执行(deferred execution)。原因如:
-
- 在一个单独的线程中运行代码;
- 多次运行代码;
- 在算法的适当位置运行代码(如,排序中的比较操作);
- 发生某种情况下执行代码(如,点击了一个按钮,数据到达,等待);
- 只在必要时运行代码;
如重复一个动作n次:
repeat(10,()-> System.out.println("Hello World!"));