【Java8】lambda表达式

定义

  Lambda表达式是一种匿名函数。简单的说,它是没有声明的方法,即没有访问修饰符、返回值类型和名字。
  

这里写图片描述

格式

( params ) -> expression
( params ) -> statement
( params ) -> { statements }

规范

  1. 一个Lambda表达式可以有零个或多个参数。
  2. 参数的类型可以明确声明,也可以不声明。
  3. 空圆括号代表参数集为空。
  4. 当只有一个参数时,圆括号可省略;当Lambda表达式的主体只有一条语句时,花括号可省略。
  5. 参数个数大于1时,所有参数必须放到圆括号内,参数之间用逗号相隔;主体包含一条以上语句,花括号不可省略。

举例

() -> 42
( int a, int b ) ->  a + b
( String s ) -> { System.out.println(s); } 等价于 s -> System.out.println(s); 
() -> System.out.println("Hello World!"); 
() -> return 3.1415;

应用

  • 创建线程
//原写法
Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World!");
    }
});
t.start();

//Lambda写法
Thread t = new Thread(() -> System.out.println("Hello World!"));
t.start();
  • 遍历集合
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

//原写法
for(Integer n: list) {
   System.out.println(n);
}

//Lambda写法
list.forEach(n -> System.out.println(n));

//Lambda另一种写法
list.forEach(System.out::println);

结合map使用

//将集合中每个值加1后输出。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.stream().map( x -> x + 1 ).forEach( System.out::println );

//将集合中每个值加1,赋值给另一个集合
List<Integer> addList = list.stream().map( x -> x + 1 ).collect(Collectors.toList());
addList.forEach(System.out::println);

结合map + reduce使用

//将集合中每个值加1,然后所有值求和,输出
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
int sum = list.stream().map( x -> x + 1 ).reduce( ( x,y ) -> x + y ).get();
System.out.println(sum);

结合filter使用

//过滤操作,过滤掉集合中小于等于4的数
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
List<Integer> filterList = list.stream().filter( x -> x>4 ).collect(Collectors.toList());
filterList.forEach(System.out::println);

排序

List<String> list = Arrays.asList("b","a","d","c");

//原写法,匿名类实现
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
});
list.forEach(System.out::println);

//Lambda写法
Collections.sort(list,(x,y) -> x.compareTo(y));
list.forEach(System.out::println);

//另一种写法
List<String> result = list.stream().sorted(( x,y ) -> x.compareTo(y)).collect(Collectors.toList());
result.forEach(System.out::println);

方法引用

  方法引用(method reference)由双冒号::标识,如:System.out::println。有三种不同的表现形式:

 1. objectName::instanceMethod
 2. ClassName::staticMethod
 3. ClassName::instanceMethod

  前两种方式类似,等同于把Lambda表达式的参数直接当成方法参数来调用。比如System.out::println等同于x -> System.out.println(x);,把Lambda表达式的参数x,当做方法参数来调用;Math::max等同于( x,y ) -> Math.max( x,y ),把Lambda表达式的参数x,y当做方法参数来调用。
  
  最后一种方式,等同于把Lambda表达式的第一个参数当成instanceMethod的目标对象,其他剩余参数当做该方法的参数。比如String::toLowerCase等同于x -> x.toLowerCase()
  
  总结:前两种方式是将传入对象当参数执行方法,后一种是调用传入对象的方法。

this关键字

  在Lambda中,this指的是声明它的外部对象。

package cn.jujianfei;

public class Main {

    public static void main(String[] args) {
        Main m = new Main();
        m.test();
    }

    public void test(){
        Thread t = new Thread(() -> System.out.println("Hello " + this.getClass().getName()));
        t.start();
    }
}

打印结果:Hello cn.jujianfei.Main

语法糖

  语法糖,又称糖衣语法,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说,使用语法糖,能够增加程序的可读性,降低程序代码出错的概率。
  
  Lambda表达式本质上是一个语法糖,由编译器推断并转换为常规的代码。

Lambda与匿名类

  Lambda可以取代匿名类。区别:this关键字指代的意义不同。匿名类中this关键字指向匿名类,Lambda中this关键字指向包围Lambda表达式的类;另一不同在编译方法,Java编译器编译Lambda表达式并将他们转化为类里的私有函数。

函数式接口

  只定义了一个抽象方法的接口称为函数式接口。如函数式接口Predicate<T>

public interface Predicate<T>{
    boolean test(T t);
}

  Lambda表达式是依靠函数式接口发挥作用的,每一个Lambda表达式都对应了一个函数式接口的实现。比如:

Thread t = new Thread(() -> System.out.println("Hello World!"));

  其中的Lambda表达式:() -> System.out.println("Hello World!"),就是实现了Runnable接口,它的run()为无参,返回值是void,Lambda表达式也是无参,返回值是void,这是因为此Lambda表达式对应了run方法的实现。每一个Lambda表达式都是实现了一个函数式接口。编译器会自动推断Lambda表达式实现了哪个接口。java8之前已经存在一些函数式接口,如:Runnable等,java8版本也推出了一个函数式接口java.util.function,其中包含了很多类,用来支持java的函数式编程,包括:Predicate<T>Function<T,R>Consumer<T>等。这些接口是Lambda表达式发挥作用的基石。

总结

  在Java8之前,如果想要将行为传入函数,仅有的选择就是匿名类。Lambda表达式的出现,取代了匿名类,允许函数式风格编写代码。上面的例子代码中,对集合框架(Java Collections Framework)进行了很多操作,是流API和Lambda表达式的结合使用,包括了对集合数据进行提取、过滤和排序等基本的流API功能。关于流API还有很多有待学习。函数式接口理解起来有些费劲,也不知有没有问题,还望多多指点。

猜你喜欢

转载自blog.csdn.net/gnd15732625435/article/details/80627721