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);

 

posted @ 2022-01-28 11:20  zhenjingcool  阅读(74)  评论(0编辑  收藏  举报