Lambda表达式
参考:http://blog.oneapm.com/apm-tech/226.html
1 背景
java是面向对象语言,在java世界里,除了几种基本数据类型外,其他都是对象。javascript是函数式编程语言,函数可以独立存在,可以将函数赋值给一个变量,或将其作为参数传递给其他函数,这种特性有很多好处。而java在1.8之前没有这个特性,带来很多不便,比如:
java swing中,为了给组件监听添加鼠标点击事件,我们定义了一个匿名类,使用这种方式我们将函数功能传递给addMouseListener。
someObject.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { //Event listener implementation goes here... } });
也即是说在java里将普通的函数像参数一样传值并不容易。而java1.8引入的Lambda表达式解决了这个问题。
2 Lambda表达式
一个 Lambda 表达式可以有零个或多个参数
参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
空圆括号代表参数集为空。例如:() -> 42
当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
Lambda 表达式的主体可包含零条或多条语句
如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
举例
(int a, int b) -> { return a + b; } () -> System.out.println("Hello World"); (String s) -> { System.out.println(s); } () -> 42 () -> { return 3.1415; }
3 函数式接口
函数式接口是只包含一个抽象方法声明的接口
例如Runnable接口就是一个函数式接口,添加了@FunctionalInterface注解。
@FunctionalInterface public interface Runnable { public abstract void run(); }
Lambda表达式一个重要特性:每个 Lambda 表达式都能隐式地赋值给函数式接口
因此,我们可以如下方式定义一个Runnable引用:
Runnable r = () -> System.out.println("hello world");
当我们创建线程时,也变得简单了:
new Thread( () -> System.out.println("hello world") ).start();
使用lambda表达式和函数式接口的例子,我们创建一个函数式接口,execute函数可以接受Lambda表达式作为参数。
@FunctionalInterface public interface WorkerInterface { public void doSomeWork(); }
public class Test { public static void execute(WorkerInterface worker) { worker.doSomeWork(); } public static void main(String [] args) { execute( () -> System.out.println("Worker invoked using Lambda expression") ); } }
4 方法引用(::)
方法引用,也就是对一个方法的引用。方法引用和Lambda表达式密切相关。
比如:
person -> person.getAge() //Lambda表达式
Person::getAge //方法引用
() -> new HashMap<>() //Lambda表达式
HsahMap::new //方法引用
Lambda表达式和方法引用都可以赋值给函数式接口。
public class Car { public static void main(String[] args) { //构造器引用:它的语法是Class::new Car car = Car.create(Car::new); //Car car = Car.create(()->new Car());//使用Lambda等效写法,运行效果一样 Car car1 = Car.create(Car::new); Car car2 = Car.create(Car::new); Car car3 = new Car(); List<Car> cars = Arrays.asList(car,car1,car2,car3); System.out.println("===================以上是构造器引用========================"); //静态方法引用:它的语法是Class::static_method,实例如下: cars.forEach(Car::collide); //cars.forEach((obj) -> collide(obj));//使用Lambda等效写法,运行效果一样 System.out.println("===================以上是静态方法引用========================"); //特定类的任意对象的方法引用:它的语法是Class::method实例如下: cars.forEach(Car::repair); //cars.forEach((obj) -> obj.repair());//使用Lambda等效写法,运行效果一样 System.out.println("==============以上是特定类的任意对象的方法引用================"); //特定对象的方法引用:它的语法是instance::method实例如下: final Car police = Car.create(Car::new); cars.forEach(police::follow); System.out.println("===================以上是特定对象的方法引用==================="); } //Supplier是jdk1.8的接口,这里和lamda一起使用了 public static Car create(final Supplier<Car> supplier) { return supplier.get(); } public static void collide(final Car car) { System.out.println("Collided " + car.toString()); } public void follow(final Car another) { System.out.println("Following the " + another.toString()); } public void repair() { System.out.println("Repaired " + this.toString()); } }
5 举例
1
new Thread( () -> System.out.println("Hello from thread") ).start();
2
button.addActionListener( (e) -> { System.out.println("The button was clicked. From Lambda expressions !"); });
3
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); list.forEach(n -> System.out.println(n)); //or we can use :: double colon operator in Java 8 list.forEach(System.out::println);
4
在下面的例子中,我们使用断言(Predicate)函数式接口创建一个测试,并打印所有通过测试的元素,这样,你就可以使用 Lambda 表达式规定一些逻辑,并以此为基础有所作为
public class SmartProduceApplication { public static void main(String[] args) { SmartProduceApplication smartProduceApplication = new SmartProduceApplication(); List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); System.out.println("Print all numbers:"); evaluate(list, (n)->true); System.out.println("\nPrint no numbers:"); evaluate(list, (n)->false); System.out.println("\nPrint even numbers:"); evaluate(list, (n)-> n%2 == 0 ); System.out.println("\nPrint odd numbers:"); evaluate(list, (n)-> n%2 == 1 ); System.out.println("\nPrint numbers greater than 5:"); evaluate(list, (n)-> n > 5 ); } public static void evaluate(List<Integer> list, Predicate<Integer> predicate) { for(Integer n: list) { if(predicate.test(n)) { System.out.print(n + " "); } } } }
输出
Print all numbers: 1 2 3 4 5 6 7
Print no numbers:
Print even numbers: 2 4 6
Print odd numbers: 1 3 5 7
Print numbers greater than 5: 6 7
5
下面的例子使用 Lambda 表达式打印数值中每个元素的平方,再使用 reduce() 将所有元素计入一个数值。注意我们使用了 .stream() 方法将常规数组转化为流。Stream是Java 8 新增加的流 APIs,能结合 Lambda 表达式产生神奇的效果
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); list.stream().map((x) -> x*x).forEach(System.out::println); int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get(); System.out.println(sum);