Lambda表达式是JDK 8开始后的一种新语法形式。

作用:简化匿名内部类的代码写法

简化格式

(匿名内部类被重写方法的形参列表) -> {
重写方法
}

Lambda表达式只能简化函数式接口的匿名内部类的写法形式

什么是函数式接口?

  • 首先必须是接口、其次接口中有且仅有一个抽象方法的形式
  • 通常会在接口上加上一个@FunctionalInterface注解,标记该接口必须是满足函数式接口

如何使用Lambda?

我们将根据下面三个问题来帮助大家理解和使用Lambda

背景:我们自定义了一个man的类,创建了一个man的List。

class man {
public int age;
public char sex;
public double socre; public man(int age, char sex, double score) {
this.age = age;
this.sex = sex;
this.score = score;
}
}

问题一

现需要对这个list根据人的年龄进行排序

要实现排序的功能,可以直接调用List对象自带的sort方法完成,但是需要man先实现Comparator的接口并重写compare方法,编译器才能比较两个不同man的大小。但是要更改原始类的代码,会比较麻烦,如果以后要对人的分数进行排序,那就又要更改的类的源码,这样操作很不方便。

sort(Comparator<? super E> c)方法可以直接传入一个Comparator对象,我们可以直接改写compare方法就可以实现比较。

第一种写法

public class lambdaTry {
public static void main(String[] args) {
List<man> humans = new ArrayList<>();
humans.add(new man(19, 'g', 98.0));
humans.add(new man(18, 'b', 95.0));
humans.add(new man(20, 'b', 96.0));
humans.add(new man(17, 'g', 97.0)); humans.sort(new Comparator<man>() {
@Override
public int compare(man o1, man o2) {
return o1.age - o2.age;
}
});
}
}

第二种写法

Lambda

我们知道Lambda是用来简化函数式接口的匿名内部类,且Comparator满足函数式接口的两个条件:

  • 首先必须是接口、其次接口中有且仅有一个抽象方法的形式
  • @FunctionalInterface注解
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
...
}

因此我们可以对上述的源码进行改写成Lambda格式

public class lambdaTry {
public static void main(String[] args) {
List<man> humans = new ArrayList<>();
humans.add(new man(19, 'g', 98.0));
humans.add(new man(18, 'b', 95.0));
humans.add(new man(20, 'b', 96.0));
humans.add(new man(17, 'g', 97.0)); humans.sort((man o1, man o2) -> {
return o1.age - o2.age;
});
}
}

改写过后代码简洁了很多。但是还可以继续简写。

Lambda表达式的省略写法

  1. 参数类型可以不写
  2. 如果只有一个参数,参数类型可以省略,同时()也可以省略
  3. 如果Lambda表达式的方法块中代码只有一行,可以省略大括号,同时省略分号。
  4. 在条件三的基础上,如果这行代码是return语句,必须省略return。

第三种写法

Lambda简写

可以看到,此表达式满足省略写法的条件,可以继续简写成如下格式。只需要一行语句就能完成

public class lambdaTry {
public static void main(String[] args) {
List<man> humans = new ArrayList<>();
humans.add(new man(19, 'g', 98.0));
humans.add(new man(18, 'b', 95.0));
humans.add(new man(20, 'b', 96.0));
humans.add(new man(17, 'g', 97.0)); humans.sort((o1, o2) -> o1.age - o2.age);
}
}

问题二

将List转换为数组

我们知道List接口有一个方法toArray方法可以实现将其转换为数组。

JDK11之后,提供了这样的一个方法,提供了一个函数式接口来让我们转换

default <T> T[] toArray(IntFunction<T[]> generator) {
return toArray(generator.apply(0));
}

IntFunction函数式接口是从JDK8之后实现的,内部只有一个apply抽象方法,是一个标准的函数式接口

@FunctionalInterface
public interface IntFunction<R> {
R apply(int value);
}

我们可以直接用lambda,完成数组的转换

public class lambdaTry {
public static void main(String[] args) {
List<man> humans = new ArrayList<>();
humans.add(new man(19, 'g', 98.0));
humans.add(new man(18, 'b', 95.0));
humans.add(new man(20, 'b', 96.0));
humans.add(new man(17, 'g', 97.0)); // 原本写法
// man[] mans = humans.toArray(new IntFunction<man[]>() {
// @Override
// public man[] apply(int value) {
// return new man[value];
// }
// }); // lambda写法
man[] mans = humans.toArray(value -> new man[value]); // 实际上用不上这样的写法,只是为了举例说明
// man[] mans = humans.toArray(new man[0]);
// man[] mans = humans.toArray(man[]::new);
// 上面两种写法都可以,传值进去的size为0不影响实际的转换,具体可以看ArrayList的toArray重写方法 }
}

问题三

输出年龄大于18的男同学的成绩

可以用forEach方法快捷实现,forEach方法来自于Iterable接口

default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}

再看Consumer接口,也是一个函数式接口

@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
...
}

具体实现

public class lambdaTry {
public static void main(String[] args) {
List<man> humans = new ArrayList<>();
humans.add(new man(19, 'g', 98.0));
humans.add(new man(18, 'b', 95.0));
humans.add(new man(20, 'b', 96.0));
humans.add(new man(17, 'g', 97.0)); // humans.forEach(new Consumer<>() {
// @Override
// public void accept(man man) {
// if (man.age >= 18 && man.sex == 'g') {
// System.out.println(man.score);
// }
// }
// }); humans.forEach(man -> {
if (man.age >= 18 && man.sex == 'g') {
System.out.println(man.score);
}
});
}
}

有时Lambda还可以继续简写成方法引用(method reference)

方法引用

方法引用通过方法的名字来指向一个方法。

方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

方法引用使用一对冒号 ::

主要分为四种:

  • 构造器引用 Class::new

    man[] mans = humans.toArray(man[]::new);
  • 静态方法引用 Class::static_method

    打印每个man(需要在man内重写toString)

    humans.forEach(System.out::println)
  • 特定类的任意对象的方法引用 Class::method

  • 特定对象的方法引用 instance::method

Java Lambda详解的更多相关文章

  1. Java集合详解3:一文读懂Iterator,fail-fast机制与比较器

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  2. Java 集合详解 | 一篇文章解决Java 三大集合

    更好阅读体验:Java 集合详解 | 一篇文章搞定Java 三大集合 好看的皮囊像是一个个容器,有趣的灵魂像是容器里的数据.接下来讲解Java集合数据容器. 文章篇幅有点长,还请耐心阅读.如只是为了解 ...

  3. Java内部类详解

    Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...

  4. 黑马----JAVA迭代器详解

    JAVA迭代器详解 1.Interable.Iterator和ListIterator 1)迭代器生成接口Interable,用于生成一个具体迭代器 public interface Iterable ...

  5. C++调用JAVA方法详解

    C++调用JAVA方法详解          博客分类: 本文主要参考http://tech.ccidnet.com/art/1081/20050413/237901_1.html 上的文章. C++ ...

  6. Java虚拟机详解----JVM常见问题总结

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  7. [转] Java内部类详解

    作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...

  8. Java面向对象详解

    Java面向对象详解 前言:接触项目开发也有很长一段时间了,最近开始萌发出想回过头来写写以前学 过的基础知识的想法.一是原来刚开始学习接触编程,一个人跌跌撞撞摸索着往前走,初学的时候很多东西理解的也懵 ...

  9. java 乱码详解_jsp中pageEncoding、charset=UTF -8"、request.setCharacterEncoding("UTF-8")

    http://blog.csdn.net/qinysong/article/details/1179480 java 乱码详解__jsp中pageEncoding.charset=UTF -8&quo ...

随机推荐

  1. Windows三种文件系统:NTFS、FAT32、FAT16的区别

    什么是文件系统? 文件系统是操作系统用于明确磁盘或分区上的文件的方法和数据结构:即在磁盘上组织文件的方法.也指用于存储文件的磁盘或分区,或文件系统种类. 举个通俗的比喻,一块硬盘就像一个块空地,文件就 ...

  2. String类型转int类型方法

    System.out.println( "Integer.parseInt(\"5\") =" + Integer.parseInt("5" ...

  3. netty系列之:EventExecutor,EventExecutorGroup和netty中的实现

    目录 简介 EventExecutorGroup EventExecutor EventExecutorGroup在netty中的基本实现 EventExecutor在netty中的基本实现 总结 简 ...

  4. Ansible安装及初始化-从零到无

    --时间:2019年1月12日 --作者:飞翔的小胖猪 前言 说明 文档指导读者在Redhat系列操作系统上安装Ansible软件及初始化配置,包括服务端及被控端的配置文件设置. 以下所有操作都在An ...

  5. Java课程设计---学生信息管理系统需求分析及总体设计

    按照软件工程实践的原则,开发大型程序需要经历需求分析.总体设计.详细设计.编码实现.系统测试.系统维护等几个阶段. 1.需求分析 本阶段是整个软件开发过程中最重要的环节.通过了解实际运行的系统或与用户 ...

  6. java 执行shell命令遇到的坑

    正常来说java调用shell命令就是用 String[] cmdAry = new String[]{"/bin/bash","-c",cmd} Runtim ...

  7. .Net Core中无处不在的Async/Await是如何提升性能的?

    一.简介 Async/Await在.Net Core中真的是无处不在,到处都是异步操作,那为什么要用?有什么作用?别人说能提升性能?网上一堆文章看的绕晕了也没说清楚, 所以这里从理论,实践,原理一个个 ...

  8. JZ-047-求 1+2+3+...+n

    标题 求 1+2+3+...+n 求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 题目链接: 求 1+2 ...

  9. JZ-025-复杂链表的复制

    复杂链表的复制 题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝, 并返回拷贝后的头结点.(注意,输 ...

  10. tp限制访问频率

    作用 通过本中间件可限定用户在一段时间内的访问次数,可用于保护接口防爬防爆破的目的. 安装 composer require topthink/think-throttle 安装后会自动为项目生成 c ...