Lambda表达式是Java 8一个非常重要的新特性。它像方法一样,利用很简单的语法来定义参数列表和方法体。目前Lambda表达式已经成为高级编程语言的标配,像Python,Swift,C#等都已经支持Lambda表达式。

在Java 8的实现中,Lambda表达式其本质只是一个“语法糖”,经过编译器推断和处理,将其转换包装为常规的Java代码,因此就像题目所写的那样,可以让你的代码更为简洁。

Lambda表达式的基本语法:(parameters) -> expression 或 (parameters) -> { statements; }

Lambda表达式并不是一个方法,它可以用来定义了一个代码块,形式上很像是Java的匿名内部类。Lambda表达式通常会赋值给一个函数式接口,函数式接口是指只有一个抽象方法的接口。Lambda表达式可以通过上下文环境来推断变量类型, 因此在使用时尽量不人为明确的指定变量类型。

举例来看,假设我们有一个List<String>类型的列表list,如果要遍历并打印列表内容,Java 7以前的代码如下:

 for (String s : list) {
System.out.println(s);
}

Java 8来实现的话:

 list.forEach((s) -> System.out.println(s));

或者

 list.forEach(System.out::println);

再看一个例子,假设我们要对list进行排序,Java 7的代码如下:

 Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String p1, String p2) {
return p1.compareTo(p2);
}
});

Java 8来实现的话:

 Collections.sort(list, (String p1, String p2) -> p1.compareTo(p2));

需要注意的是,Lambda表达式可以做参数类型推断,这里我们可以充分利用这一点,p1和p2参数前面的String是不需要的,因此可以简化一步如下:

 Collections.sort(list, (p1,p2) -> p1.compareTo(p2));

更进一步:

 list.sort((p1,p2) -> p1.compareTo(p2));

是不是简洁了很多:)

Lambda表达式也可以用来代替匿名类。例如我们要实现Runnable接口,Java 7的代码如下:

 new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
}).start();

Java 8来实现的话:

 new Thread(() -> System.out.println("Hello world !")).start();

用Lambda表达式来实现Runnable,将五行代码转换成一行语句。

合理使用Lambda表达式,不仅能简化几行代码,还能做到合理的代码抽象。当我们在实现的两个很大的方法时,如果大部分的代码都是相同的,只有一小点代码不一样时,我们可以通过将Lambda表达式作为参数传入,以达到不同表意的目的。

前面提到的函数式接口(Functional Interfaces),它表示只有一个抽象方法的接口,可以用来指向Lambda表达式。例如:

 Consumer c = (s) -> System.out.println(s);

Java 8在java.util.function包中实现了新的几个:

  • Function<T, R>:接受一个参数T,返回结果R

  • Predicate<T>:接受一个参数T,返回boolean

  • Supplier<T>:不接受任何参数,返回结果T

  • Consumer<T>:接受一个参数T,不返回结果

  • UnaryOperator<T>:继承自Function<T, T>,接受一个参数T,返回相同类型T的结果

  • BiFunction<T, U, R>:接受两个参数T和U,返回结果R

  • BinaryOperator<T>:继承自BiFunction<T, T, T>,接受两个相同类型T的参数,返回相同类型T的结果

  • ……

另外,我们最为熟悉的函数式接口还有:

  • Runnable:实际上是不接受任何参数,也不返回结果

  • Comparable<T>:实际上是接受两个相同类型T的参数,返回int

  • Callable<V>:不接受任何参数,返回结果V

通常我们应该尽量使用标准的函数式接口,如果我们要自定义的话,可以使用@FunctionalInterface注解,例如:

 @FunctionalInterface
public interface funcInterface {
public abstract B op(A a);
}

在将函数式接口作为参数时,需要注意尽量避免方法重载。由于Lambda表达式根据所在环境的目标类型来决定Lambda表达式的类型(也就是Target Typing), 因此方法重载有时会导致编译器犯晕。我们可以使用不同的方法名来解决这个问题。

在这里,我们还需要澄清几点:

  • Lambda表达式并不是函数式接口。它能赋值给函数式接口,是因为编译器将它包装成了对应的函数式接口;

  • 更进一步,Lambda表达式也不是匿名类:

    • 它并没有定义新的作用域,外面定义的局部变量在Lambda表达式内部是可见的;

    • 它不能改变外部变量的值,只能读取final或者effectively final的变量;

    • 它不能前向读取外部变量,也就是只有在外部变量申明之后才能读取,而在匿名内部类是可以的;

Java 8 还增强了对集合数据的批量操作Stream,通常会和Lambda表达式一起使用。Lambda表达式和 Stream 可以说是Java语言从添加泛型(Generics)和注解(annotation)以来最大的变化了。下一篇文章将重点介绍Stream。

Java 常用的几个lambda表达式的更多相关文章

  1. Java 终于在 Java 8 中引入了 Lambda 表达式。也称之为闭包或者匿名函数。

    本文首发于 blog.zhaochunqi.com 转载请注明 blog.zhaochunqi.com 根据JSR 335, Java 终于在 Java 8 中引入了 Lambda 表达式.也称之为闭 ...

  2. Java 关于函数式接口与Lambda表达式之间的关系

    java是一种面向对象的语言,java中的一切都是对象,即数组,每个类创建的实例也是对象.在java中定义的函数或方法不可能完全独立,也不能将方法函数作为参数或返回值给实例. 在java7及以前,我们 ...

  3. 恕我直言你可能真的不会java第1篇:lambda表达式会用了么?

    本文配套教学视频:B站观看地址 在本号之前写过的一些文章中,笔者使用了lambda表达式语法,一些读者反映说代码看不懂.本以为java 13都已经出了,java 8中最重要特性lambda表达式大家应 ...

  4. Java 8新特性-3 Lambda 表达式

    在 Java 8 之前,匿名内部类,监听器和事件处理器的使用都显得很冗长,代码可读性很差. 在Java 8 之前使用匿名内部类: 例如 interface ITestPrint{ public voi ...

  5. [Java] 设计模式:代码形状 - lambda表达式的一个应用

    [Java] 设计模式:代码形状 - lambda表达式的一个应用 Code Shape 模式 这里介绍一个模式:Code Shape.没听过,不要紧,我刚刚才起的名字. 作用 在应用程序的开发中,我 ...

  6. Java 8 新特性之 Lambda表达式

    Lambda的出现就是为了增强Java面向过程编程的深度和灵活性.今天就来分享一下在Java中经常使用到的几个示例,通过对比分析,效果应该会更好. – 1.实现Runnable线程案例 其存在的意义就 ...

  7. Effective Java 第三版——42.lambda表达式优于匿名类

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  8. Java编程的逻辑 (91) - Lambda表达式

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  9. Java之线程池和Lambda表达式

    线程池和lambda表达式 学习线程池和lambda表达式的理解 补充一个知识点(单例设计模式) 在多线程中,我们只需要一个任务类,为了防止创建多个任务类,这个时候就需要用到单例模式,单例模式有两种设 ...

随机推荐

  1. mongodb3.6集群搭建:分片集群认证

    上篇集群已经创建,现在加入认证. 1. 生成密钥文件每个服务器上创建路径: mkdir -p /var/lib/mongo/auth 生成64字节的密钥文件openssl rand -base64 6 ...

  2. Python学习笔记4-os,sys模块

    一.os模块 import os print(os.getcwd())#取当前工作目录 os.chmod("/usr/local",7)#给文件/目录加权限 print(os.ch ...

  3. JAVA多线程提高十四: 面试题

    前面针对多线程相关知识点进行了学习,那么我们来来看看常见的面试题: 1. 空中网面试题1 package com.kongzhongwang.interview; import java.util.c ...

  4. MapReduce (MRV1)设计理念与基本架构

    MapReduce 是一个分布式计算框架,主要由两部分组成:编程模型和运行时环境. 其中,编程模型为用户提供了非常易用的编程接口,用户只需要像编写串行程序一样实现几个简单的函数即可实现一个分布式程序, ...

  5. zlib打印bit length overflow

    bit length overflow code bits -> code bits -> zlib库输出此log,此log不代表压缩出现错误,没有什么危害,而且zlib非常稳定,完全可以 ...

  6. 转 -- ARM 中 LDR伪指令

    我们知道ARM CPU中有一条被广泛使用的指令LDR,它主要是用来从存储器(确切地说是地址空间)中装载数据到通用寄存器.但不论是ARMASM还是GNU ARM AS,都提供了一条与之同名的伪指令LDR ...

  7. 【leetcode 简单】 第七十二题 各位相加

    给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数. 示例: 输入: 38 输出: 2 解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2. 由于 2 是一位数,所 ...

  8. HDU 3535 AreYouBusy (混合背包之分组背包)

    题目链接 Problem Description Happy New Term! As having become a junior, xiaoA recognizes that there is n ...

  9. mysql 数据范围总结

    MEDIUMINT 带符号的范围是-8388608到8388607,无符号的范围是0到16777215,使用3个字节. 一般情况下推荐使用 unsigned (无符号,即只接受正整数)

  10. 关于Unix/Linux的终端、伪终端、控制台和shell

    历史是什么:是过去传到将来的回声,是将来对过去的反映. ——雨果(法)<笑面人> 阅读本文大概需要花费你15分钟 文章导航: 计算机的发展 UNIX系统的诞生 UNIX系统的发展 终端与控 ...