飞道的博客

Java JDK8新特性之Lambda表达式

440人阅读  评论(0)

一、初步了解Lambda表达式

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性之一。

如果不了解python的话,初次学习Lambda表达式肯定有些难以理解。

简单来说,Lambda 表达式是一个匿名函数,即没有函数名的函数。它允许把函数a作为一个函数b的参数(函数作为参数传递到函数中),使用 Lambda 表达式可以使代码变的更加简洁紧凑。
不过往往简洁的东西都比较难理解 orz

二、Lambda表达式与匿名内部类

正式介绍Lambda表达式之前我们先用匿名内部类实现下比较两个Integer值的大小
(匿名内部类的详解见我的另一篇博客,我会写的,相信我)

@Test
public void test1(){
    //匿名内部类写法
    Comparator<Integer> com = new Comparator<>(){
    	@Override
    	public int compare(Integer x1,Integer x2){
    	    //比较两个Integer值大小
    	    return Integer.compare(x1,x2);
    	}
    };
    TreeSet<Integer> tree = new TressSet<>(com);
}

现在我们来看一下用Lambda表达式该怎么实现

@Test
public void Test2(){
    //Lambda表达式写法
    Comparator<Integer> com = (Integer x1,Integer x2) -> Integer.compare(x1,x2);
    TreeSet<Integer> tree = new TreeSet<>(com);
}

来看一下匿名内部类和Lambda表达式的对比

由此可见Lambda表达式是由2部分组成的

  1. 参数列表:黄色框,相当于匿名内部类中函数的参数
  2. Lambda体:绿色框,相当于匿名内部类中的函数体

(值得一提的是, 红色框的内容虽然相同,但是它并不属于Lambda表达式,Lambda表达式指的是等号“ = ”右边的内容,初学者要注意一下)

这2部分用“ -> ”连接,即:

(参数列表) -> {Lambda体}

可以说Lambd表达式是匿名内部类的一种简化写法

来练习一下Lambda表达式创建线程的写法 (^ _ ^)

@Test
public void test1() {
    int num = 0;//jdk1.7之前final必须显示写出来
    //匿名内部类创建线程,创建Runnable接口实现类的实例,重写run方法
    Runnable r = new Runnable() {
        public void run() {
            System.out.println("hello Runnable"+num);
        };
    };
    r.run();
    //改写成Lambda表达式的形式
    Runnable r1 = () -> System.out.println("hello Lambda"+num);//无参数,无返回值
    r1.run();
}

三、Lambda表达式的基本语法

刚才我们已经总结了Lambda表达式的整体构造为

(参数列表) -> {Lambda体}

这里可能有人会奇怪说前面的例子中Lambda体没有带大括号{},为什么这里加上了,别着急,往下看

因为Lambda表达式是对匿名内部类中函数的简化写法,函数的有无参、有无返回值等都会对Lambda表达式的语法产生一定的影响

“ -> ”,叫做箭头操作符或者Lambda操作符,它把“ = ”右边的表达式分成两部分

左为参数列表:
Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文可以推断出参数类型;

右为Lambda体:
表达式执行的功能,即Lambda体,Lambda体就像是函数体,里面可以写一条或多条语句。

  1. 无参,无返回值
//无参数,无返回值
Runnable r1 = () -> System.out.println("hello Lambda");
  1. 有一个参数,无返回值
    当参数只有一个时,可以省略参数的括号
Consumer<String> con = (x)->System.out.println(x);
//只有1个参数可省略括号
Consumer<String> con = x -> System.out.print(x);
  1. 两个以上参数,有返回值,Lambda体中有多条语句
Comparator<Integer> com = (x,y)->{
   System.out.println("函数式接口");
   return Integer.compare(x, y);
  };

当Lambda体中的语句只有一条时,大括号{}可以省去

Comparator<Integer> com = (x,y)-> Integer.compare(x, y);

我把Lambda表达式的写法总结了一副对联:
上联:左右遇一括号省
下联:左侧推断类型省
横批:省就vans

四、函数式接口

Lambda表达式需要“函数式接口”的支持

函数式接口:接口中只有一个抽象方法的接口,成为函数式接口。
可以使用@FunctionalInterface 修饰可以检查接口是不是函数式接口

(函数式接口在这里就简单提一下,我会写另一篇博客详细介绍的)

五、Lambda表达式的方法引用

5.1 方法引用

若Lambda体中的功能,已经有方法提供了实现,可以使用方法引用
可以将方法引用理解为lambda表达式的另外一种表现形式
写法有三

  1. 对象引用 :: 实例方法名
@Test
public void test1() {
    PrintStream ps = System.out;
    //Consumer是函数式接口的一种,叫消费接口
    Consumer<String> con = (str) -> ps.println(str);
    con.accept("Hello world");
    //改写成 对象引用::实例方法名
    Consumer<String> con2 = ps::println;
    con2.accept("Hello world");
}

@Test
public void test2() {
    //Employee类自己随便写一个就行
    Employee emp = new Employee(101, "张三", 18, 9999.99);
    //Lambda表达式写法
    Supplier<String> sup = () -> emp.getName();
    //System.out.println(sup.get());
    //改写成方法引用 对象引用::实例方法名
    Supplier<String> sup2 = emp::getName;
    System.out.println(sup2.get());
}
  1. 类名::静态方法名
@Test
public void test3() {
    //Lambda表达式写法
    BiFunction<Double,Double,Double> fun = (x,y) -> Math.max(x,y);
    System.out.println(fun.apply(1.5, 2.5));
    //方法引用 类名::静态方法名
    BiFunction<Double,Double,Double> fun1 = Math::max;
    System.out.println(fun.apply(1.5, 2.5));
 }
 
@Test
public void test4() {
    //Lambda表达式写法
    Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
    //方法引用 类名::静态方法名
    Comparator<Integer> com2 = Integer::compare;
}
  1. 类名::实例方法名
@Test
public void test5() {
    //Lambda表达式写法
    BiPredicate<String, String> bp = (x,y) -> x.equals(y);
    System.out.println(bp.test("abcde", "abcde"));
    //改写成方法引用 类名::实例方法名
    BiPredicate<String, String> bp1 = String::equals;
    /*——————————————————我是分割线————————————————————*/
    //Lambda
    Function<Employee, String> fun = (e) -> e.show();
    System.out.println(fun.apply(new Employee()));
    //方法引用
    Function<Employee, String> fun2 = Employee::show;
    System.out.println(fun2.apply(new Employee()));
    /*——————————————————我是分割线————————————————————*/
    Employee e = new Employee(100, "刘能", 59, 2000.34);
    Supplier<String> fun3 = e::show;
}

5.2 构造器引用

构造器中的参数列表与函数式接口一致

写法:类名::new

@Test
public void test6() {
    //Lambda
    Supplier<Employee> sup = () -> new Employee();//Supplier接口获取一个类型,获取Employee类型
    System.out.println(sup.get());
    //构造器引用
    Supplier<Employee> sup2 = Employee::new;
    System.out.println(sup2.get());
}

5.3 数组引用

写法:类型[ ] :: new

@Test
public void test7() {
  //R apply(T t);
  Function<Integer, String[]> fun =(args) -> new String[args];//可实现可变长数组
  String[] strs = fun.apply(10);
  System.out.println(strs.length);
  
  Function<Integer, String[]> fun2 = String[]::new;
  Function<Integer, Employee[]> fun3 = Employee[]::new;
  System.out.println(fun2.apply(12).length);
  System.out.println(fun3.apply(13).length);
}

转载:https://blog.csdn.net/becatjd/article/details/105081075
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场