一文带你彻底搞懂Lambda表达式

1. 为什么使用Lambda表达式

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

我们来看一下使用lambda之前创建匿名内部类:

new Thread(new Runnable() {
  @Override
  public void run() {
    System.out.println("执行Runnable方法");
  }
});

lambda表达式:

new Thread(() -> System.out.println("执行Runnable方法")); // 无参Lambda表达式

在Idea中遇到可以转换lambda的代码会有灰色提示,按alt+enter可自动转换:

2. Lambda语法

Java8中引入了一个新的操作符"->”该操作符称为箭头操作符或Lambda 操作符。

箭头操作符将Lambda表达式拆分成两部分:

左侧: Lambda表达式的参数列表

右侧: Lambda表达式中所需执行的功能,即Lambda体

示例:

语法格式一:无参数,无返回值。

Runnable r = () -> System.out.println("Hello World");

r.run();

语法格式二:有一个参数,并且无返回值。

Consumer consumer = (x) -> System.out.println(x);
//或者写成: x -> System. out.println(x); 只有一个参数,括号可以省略
consumer.accept("Hello");

语法格式三:有两个以上的参数,有返回值,并且Lambda体中有多条语句。

Comparator<Integer> comparator = (x, y) -> {
  System.out.println("Hello");
  return Integer.compare(x, y);
};
TreeSet<Integer> treeSet = new TreeSet<>(comparator);

语法格式四:若Lambda体中只有一条语句,return 和大括号都可以省略不写,Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即类型推断

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

关于类型推断,其实在数组中我们早已经使用过:

String[] strArr = {"a","b", "c"}; // 类型推断
// 写成下面这种方式则编译报错
String [] strArr;
strArr = {"a","b", "c"};
// 还有List的类型推断:
List<String> list = new ArrayList<>(); 

3. 函数式编程接口

上面我们看到的lambda表达式,其实都依赖于接口的支持,lambda表达式的本质是对接口的一种实现。这种接口称为函数式接口,即接口中只有一个抽象方法的接口。函数式接口可以使用@FunctionInterface注解修饰,表示被修饰的接口必须是函数式接口。

// 函数式接口只能有一个抽象方法
@FunctionalInterface
public interface FuncTest {
  void accept(Object o);
}

如果在函数式接口中写两个以上的方法,编译会报错:

我们来自己写一个实际的例子了解下函数式接口的使用:

// 1.写一个对传入参数进行操作的函数式接口
@FunctionalInterface
public interface FuncTest {
  void operation(Integer x);
}

// 2.写一个方法,将函数式,接口作为参数
private static Integer operate(Integer a, FuncTest funcTest) {
  return funcTest.operation(a);
}

// 3.使用函数式接口
System.out.println(operate(1, (x) -> x + x)); // 输出 2

可以看到,我们在第3步才开始定义函数式接口的实际功能,对两个数进行相加操作并返回结果。lambda的最大便捷之处就在于此,将代码作为参数传递,非常灵活,大大精简我们的代码。

4. Java8内置的4种常用函数式接口

Consumer :消费型接口

方法:void accept(T t);

Consumer<String> consumer = x -> System.out.println(x);

consumer.accept("Hello"); // 打印Hello

Supplier :供给型接口

方法:T get();

Supplier<String> supplier = () -> "Hello";

System.out.println(supplier.get()); // 打印Hello

Function<T, R> :函数型接口

方法:R apply(T t);

Function function = x -> "Hello " + x;

System.out.println(function.apply("World"));

Predicate :断言型接口

方法:boolean test(T t);

Predicate predicate = x -> x == "Hello";

System.out.println(predicate.test("Hello"));

ava.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:

5. 总结

Java8引入lambda表达式是接收了函数式编程语言的思想,例如scala之类的,它将函数视为一等公民,可以使用高阶函数等。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里函数的计算可随时调用。

lambda表达式可以使代码看起来简洁,但一定程度上增加了代码的可读性以及调试的复杂性,所以在使用时应尽量是团队都熟悉使用,要么干脆就别用,不然维护起来是件较痛苦的事。

以上这篇一文带你彻底搞懂Lambda表达式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java8 forEach结合Lambda表达式遍历 List操作

    我就废话不多说了,大家还是直接看代码吧~ @Test void testJava8ForeachMap() { Map<String, Integer> items = new HashMap<>(); items.put("A", 10); items.put("B", 20); items.put("C", 30); items.put("D", 40); items.put("E&quo

  • java lambda 表达式中的双冒号的用法说明 ::

    双冒号运算就是Java中的[方法引用],[方法引用]的格式是 类名::方法名 注意是方法名哦,后面没有括号"()"哒.为啥不要括号,因为这样的是式子并不代表一定会调用这个方法.这种式子一般是用作Lambda表达式,Lambda有所谓懒加载嘛,不要括号就是说,看情况调用方法. 例如 表达式: person -> person.getAge(); 可以替换成 Person::getAge 表达式 () -> new HashMap<>(); 可以替换成 HashMa

  • 利用Lambda表达式创建新线程案例

    代码 public class LamdaDemo { public static void main( String[] args ) { Runnable task = () -> { String threadName = Thread.currentThread().getName(); System.out.println("Hello " + threadName); }; task.run(); Thread thread = new Thread(task); t

  • Java8 Lambda表达式模板方法实现解析

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许多java框架中大量使用注解,如Hibernate.Jersey.Spring.注解作为程序的元数据嵌入到程序当中.注解可以被一些解析工具或者是编译工具进行解析.我们也可以声明注解在编译过程或执行时产生作用. 在使用注解之前,程序源数据只是通过java注释和javadoc,但是注解提供的功能要远远超

  • 解决mybatis-plus3.1.1版本使用lambda表达式查询报错的方法

    最近项目中使用了mybatis-plus 3.1.1版本,发现使用lambda表达式方式的条件构造器,执行时会报错:但是我用单元测试却通过,真是一个大坑啊.经过在网上一顿猛查,发现压根就是没有找到类似的问题,所以今天就记录一下这个大坑. 测试代码: @Override public User getByUsername(String username) { return super.getOne(new QueryWrapper<User>().lambda().eq(User::getUse

  • java 8 lambda表达式中的异常处理操作

    简介 java 8中引入了lambda表达式,lambda表达式可以让我们的代码更加简介,业务逻辑更加清晰,但是在lambda表达式中使用的Functional Interface并没有很好的处理异常,因为JDK提供的这些Functional Interface通常都是没有抛出异常的,这意味着需要我们自己手动来处理异常. 因为异常分为Unchecked Exception和checked Exception,我们分别来讨论. 处理Unchecked Exception Unchecked exc

  • 使用Java 8 Lambda表达式将实体映射到DTO的操作

    当我们需要将DTO转换为实体(Hibernate实体等)并向后转换时,我们都会面临混乱的开销代码. 在我的示例中,我将用Java 8演示代码如何变得越来越短. 让我们创建目标DTO: public class ActiveUserListDTO { public ActiveUserListDTO() { } public ActiveUserListDTO(UserEntity userEntity) { this.username = userEntity.getUsername(); ..

  • 一文带你彻底搞懂Lambda表达式

    1. 为什么使用Lambda表达式 Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升. 我们来看一下使用lambda之前创建匿名内部类: new Thread(new Runnable() { @Override public void run() { System.out.println("执行Runnable方法"); } });

  • 一文带你彻底搞懂Docker中的cgroup的具体使用

    目录 什么是cgroup cgroup的组成 cgroup提供的功能 限制cgroup中的CPU 限制cgroup中的内存 限制cgoup的进程数 前言 进程在系统中使用CPU.内存.磁盘等计算资源或者存储资源还是比较随心所欲的,我们希望对进程资源利用进行限制,对进程资源的使用进行追踪.这就让cgroup的出现成为了可能,它用来统一将进程进行分组,并在分组的基础上对进程进行监控和资源控制管理. 什么是cgroup Linux CGroup(Linux Contral Group),它其实是Lin

  • 一文带你掌握Java8中Lambda表达式 函数式接口及方法构造器数组的引用

    目录 函数式接口概述 函数式接口示例 1.Runnable接口 2.自定义函数式接口 3.作为参数传递 Lambda 表达式 内置函数式接口 Lambda简述 Lambda语法 方法引用 构造器引用 数组引用 函数式接口概述 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象. 可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口.同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口.

  • 一文带你彻底搞懂JavaScript正则表达式

    目录 正则表达式的概述 什么是正则表达式 正则表达式的作用 正则表达式的特点 正则表达式在js中的使用 创建正则表达式 测试正则表达式 test 正则表达式中的特殊字符 正则表达式的组成 边界符 字符类 量词符 预定义类 正则表达式的替换 开发中常用正则表达式 小结 正则表达式的概述 什么是正则表达式 正则表达式( Regular Expression ) 是用于匹配字符串中字符组合的模式.在js中,正则表达式也是对象! 正则表达式的作用 正则表达式通常被用来检索.替换那些符合某个模式(规则)的

  • 一篇文章带你彻底搞懂VUE响应式原理

    目录 响应式原理图 编译 创建compile类 操作fragment 获取元素节点上的信息 获取文本节点信息 操作fragment 响应式 数据劫持 收集依赖 响应式代码完善 Dep类 全局watcher用完清空 依赖的update方法 需要注意的一个地方 双剑合璧 总结 首先上图,下面这张图,即为MVVM响应式原理的整个过程图,我们本篇都是围绕着这张图进行分析,所以这张图是重中之重. 响应式原理图 一脸懵逼?没关系,接下来我们将通过创建一个简单的MVVM响应系统来一步步了解这个上图中的全过程.

  • 一篇文章带你彻底搞懂Redis 事务

    目录 Redis 事务简介 Redis 事务基本指令 实例分析 Redis 事务与 ACID 总结 Redis 事务简介 Redis 只是提供了简单的事务功能.其本质是一组命令的集合,事务支持一次执行多个命令,在事务执行过程中,会顺序执行队列中的命令,其他客户端提交的命令请求不会插入到本事务执行命令序列中.命令的执行过程是顺序执行的,但不能保证原子性.无法像 MySQL 那样,有隔离级别,出了问题之后还能回滚数据等高级操作.后面会详细分析. Redis 事务基本指令 Redis 提供了如下几个事

  • 带你彻底搞懂python操作mysql数据库(cursor游标讲解)

    1.什么是游标? 一张图讲述游标的功能: 图示说明: 2.使用游标的好处? 如果不使用游标功能,直接使用select查询,会一次性将结果集打印到屏幕上,你无法针对结果集做第二次编程.使用游标功能后,我们可以将得到的结果先保存起来,然后可以随意进行自己的编程,得到我们最终想要的结果集. 3.利用python连接数据库,经常会使用游标功能 1)以python连接mysql数据库为例 2)使用游标的操作步骤 首先,使用pymysql连接上mysql数据库,得到一个数据库对象. 然后,我们必须要开启数据

  • 带你彻底搞懂JavaScript的事件流

    目录 DOM事件流 事件冒泡 事件捕获 情景一:直接在HTML中绑定事件 情景二:[domNode].onclick()方式——DOM0级 情景三:[domNode].addEventListener()方式——DOM2级 总结 DOM事件流 要明白事件流,首先我们要明白三点: 元素不是独立的,是串联在一起的 单个元素触发事件以后还会影响其他元素 事件流的方式:事件捕获(网景提出).事件冒泡(IE提出) 我们就以上图为例,假设你给div绑定了点击事件,当你点击了div后,其他元素也会受之牵连,会

  • 一篇文章带你轻松搞懂Golang的error处理

    目录 Golang中的error error的几种玩法 哨兵错误 自定义错误类型 Wrap error Golang1.13版本error的新特性 errors.UnWrap() errors.Is() errors.As() error处理最佳实践 优先处理error 只处理error一次 不要反复包装error 不透明的错误处理 简化错误处理 bufio.scan errWriter 何时该用panic 小补充 总结 Golang中的error Golang中的 error 就是一个简单的接

  • 一文彻底搞懂Java和JDK的版本命名问题

    Java是面向对象的编程语言,在我们开发Java应用的程序员的专业术语里,Java这个单词其实指的是Java开发工具,也就是JDK(Java Development Kit).所以我们常常在CSDN等各大程序员论坛讨论到安装Java8或者JDK8或者JDK1.8或J2SE8或J2SE1.8或J2SE8或J2SE1.8,其实这3个专业词汇的概念是一样的. 告诉庆哥,你对Java的版本号以及JDK的命名真正清楚嘛?比如: Java8 Java SE 8.0 JDK1.8 -- 知道这些是怎么回事嘛?

随机推荐