1.引入lambda表达式的重要性

  lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。

  在前面的回调部分,有一个例子是,ActionListener类实现了TimePrinter接口并在类中定义了一些方法作为到达时间后要发生的动作。然后将listener对象传递给Timer类的构造器中,启动构造器之后,一定的时间间隔后就可以执行ActionListener类中的动作方法。

class TimePrinter implements ActionListener
{  
   public void actionPerformed(ActionEvent event)
   {  
      System.out.println("At the tone, the time is " + new Date());
   }
}

  还有一个例子是,如果要根据长度而不是默认的字典顺序对字符串排序,使用传统的接口的方法就是先定义一个实现了Comparator接口的类LengthComparator,然后实现其中的compare方法,然后创建一个LengthComparator的实例,最后将这个对象传递给Array.sort方法就可以实现按照字符串大小的顺序排列。

class LengthComparator implement Comparator<String>
{
public int compare(String first, String second){
return first.length() - second.length();
}
} ...
Arrays.sort(strings, new LengthComparator)

  这两个例子的共同点就是,都是将一个代码块传递到某个对象(一个是定时器,一个是sort方法),而这个代码将会在将来的某个时间被调用。sort方法调用compare方法也不是立即调用的,而是在数组排序完成前,才会一直调用compare方法,如果顺序不正确就会重新排列元素。

  如果不使用lambda表达式,在Java中不能直接传递代码段,必须创建一个对象,而这个对象的类需要一个方法能包含要传递的代码。

  2.lambda表达式语法

  上面的LengthComparator类中的compare中的方法用lambda表达式表示就是:

(first, second) -> first.length() - second.length()

  而ActionListener类中的actionPerformed方法用lambda表达式表示就是:

ActionListener listenter = event ->
System.out.println("The time is " + new Date())

  这里可以看出lambda表达式的基本语法就是:(参数1类型  参数1, 参数2类型 参数2,...)  -> 表达式或代码块。

  例如:

  ①如果代码无法放在一个表达式中,则可以把这些代码放在大括号{}中。如果只在某些分支返回值,那么就不合法,例如没有下面的else代码,表达式就不合法。

(String first, String second)  ->
{
if(first.length() < second.length() return -1;
else return 0;
}

  ②即使没有参数,也仍然要提供括号,就像没有参数的方法一样

() -> {for(int i = 1; i < 10; i++) System.out.print(i)};

  ③lambda表达式不需要执行返回值类型,可以根据上下文推导得出。

  举例:

package lambda;

import java.util.*;

import javax.swing.*;
import javax.swing.Timer; /**
* This program demonstrates the use of lambda expressions.
* @version 1.0 2015-05-12
* @author Cay Horstmann
*/
public class LambdaTest
{
public static void main(String[] args)
{
String[] planets = new String[] { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" };
System.out.println(Arrays.toString(planets)); // 打印:[Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune]
Arrays.sort(planets);
System.out.println(Arrays.toString(planets)); // 按字典顺序的默认排序:[Earth, Jupiter, Mars, Mercury, Neptune, Saturn, Uranus, Venus]
System.out.println("Sorted by length:");
Arrays.sort(planets, (first, second) -> first.length() - second.length());
System.out.println(Arrays.toString(planets));// 按字符串长度排序:[Mars, Earth, Venus, Saturn, Uranus, Jupiter, Mercury, Neptune] Timer t = new Timer(1000, event -> System.out.println("The time is " + new Date()));
t.start(); // 每个1秒,打印:The time is Wed Jul 18 20:09:40 GMT+08:00 2018...
}
}

  3.函数式接口

  像前面的例子中,当表达式需要一个接口类的对象,并且这个接口类只有一个方法时,就可以提供一个lambda表达式,这种接口称为函数式接口。

  还是以Array.sort方法来分析:

Arrays.sort(planets, (first, second) -> first.length() - second.length());

  底层的实现过程是:Arrays.sort方法会接收实现了Comparator<string>的某个类的对象。在这个对象上调用compare方法会执行这个lambda表达式的体。这些对象和类的管理完全取决于具体实现,与使用传统的内联类相比,这样可能要高效得多。实际上,最好把lambda表达式看成是一个函数,而不是一个对象。

  4.方法引用

  如果已经有现成的方法可以完成想要传递到其他代码中的某个动作,可以使用方法引用。

  例如:在定时器中定时打印这个动作事件对象本身,使用传统的lambda表达式是:

Timer t = new Timer(1000, event -> System.out.println(event));

  如果使用方法引用的话,可以写成这样:和上面的表达等价

Timer t = new Timer(1000, System.out::println);

  总结来说,就是x -> System.out.println(x)等价于System.out::println,相当于把println方法传递了进去,所以叫方法引用。

  方法引用的四种类型:

  • object::instanceMethod(obj::show等价于() -> obj.show())
  • Class::staticMethod(x -> System.out.println(x)等价于System.out::println;Math::pow等价于(x,y->Math.pow(x,y))
  • Class::instanceMethod(String::compareToIgnoreCase等价于(x,y)->x.compareToIgnoreCase(y))
  • this和super(this::equals等价于x -> this.equals(x);Timer t = new Timer(1000, super::greet

  5.构造器引用
  构造器引用于方法名引用类似,只不过方法名是new而已。

  例如,

  Person::new是Person构造器的一个引用,如果有多个构造器,将会自动从上下文推导出一个构造器。

  int[]::new是等价于lambda表达式x -> new int[x]

  

  6.变量作用域

  lambda表达式可以捕获外围作用域中变量的值,但是有一些限制:

  • 只能引用值不会改变的变量。
  • 只能引用不会在外部改变的变量。
  • 变量必须是最终变量,即变量初始化后不会再为它赋值
  • 不能声明一个与外部局部变量同名的变量
  • 不能有两个同名的局部变量
  • lambda表达式中this关键字是指lambda表达式所在的类对象。

  

  7.应用lambda表达式的Comparator接口

  Comparator接口中包含很多方便的静态方法来创建比较器,这些方法可以用lambda表达式表达或引用。

  (1)假设有一个Person对象数组,要按名字对这些对象进行排序:

Arrays.sort(people, Comparator.comparing(Person::getName));

  (2)比较名字的另一种表达,先比较姓,如果相等再比较名:

Arrays.sort(people, Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName));

  (3)根据人名的长度比较排序:

Arrays.sort(people, Comparator.comparing(Person::getName), (s,t)->Integer.compare(s.length(), t.length());

  (4)遇到中文名字null时不会抛出异常,并且按照自然顺序比较(按自然顺序逆序reverseOrder()):

Arrays.sort(people, Comparator.comparing(Person::getMiddleName), Comparator.nullFirst(naturalOrder());

Java基础(十二)lambda表达式的更多相关文章

  1. Java基础十二--多态是成员的特点

    Java基础十二--多态是成员的特点 一.特点 1,成员变量. 编译和运行都参考等号的左边. 覆盖只发生在函数上,和变量没关系. Fu f = new Zi();System.out.println( ...

  2. Java基础教程:Lambda表达式

    Java基础教程:Lambda表达式 本文部分内容引用自OneAPM:http://blog.oneapm.com/apm-tech/226.html 引入Lambda Java 是一流的面向对象语言 ...

  3. Java基础教程(23)--lambda表达式

    一.初识lambda表达式 1.定义   lambda表达式是一个可传递的代码块,或者更确切地说,可以把lambda表达式理解为简洁地表示可传递的匿名方法的一种方式.它没有名称,但它有参数列表.函数主 ...

  4. Util应用程序框架公共操作类(十二):Lambda表达式公共操作类(三)

    今天在开发一个简单查询时,发现我的Lambda操作类的GetValue方法无法正确获取枚举类型值,以至查询结果错误. 我增加了几个单元测试来捕获错误,代码如下. /// <summary> ...

  5. Java基础(十二)--clone()方法

    Clone在Java中就是用来复制对象,通过分配一个和源对象相同大小的内存空间,然后创建一个新的对象,那么他和=的区别在哪? 通过=实现对象拷贝: @Data @NoArgsConstructor @ ...

  6. java基础(十二 )-----Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...

  7. Java基础(十二)之包和权限访问

    软件包 软件包解决了两个类名字一样的问题.软件包就是一个"文件夹". 包名的命名规范:1.要求所有字母都小写:2.包名一般情况下,是你的域名倒过来写.比如baidu.com,pac ...

  8. java基础十二[集合与泛型](阅读Head First Java记录)

    集合 List 知道索引顺序的集合,ArrayList.LinkedList.Vector三个子类实现了List接口   ArrayList ArrayList没有排序方法,可以用Collection ...

  9. Java 基础(二)| 使用 lambad 表达式的正确姿势

    前言 为跳槽面试做准备,今天开始进入 Java 基础的复习.希望基础不好的同学看完这篇文章,能掌握 lambda 表达式,而基础好的同学权当复习,希望看完这篇文章能够起一点你的青涩记忆. 一.什么是 ...

  10. Java实习生常规技术面试题每日十题Java基础(二)

    目录 1. JAVA 的反射机制的原理. 2.静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同? 3.如何将String类型转化成Number类型. 4.什 ...

随机推荐

  1. Spring boot集成Rabbit MQ使用初体验

    Spring boot集成Rabbit MQ使用初体验 1.rabbit mq基本特性 首先介绍一下rabbitMQ的几个特性 Asynchronous Messaging Supports mult ...

  2. Android Adapter的一些记录

    一.摘要 An Adapter object acts as a bridge between an AdapterView and the underlying data for that view ...

  3. 死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

  4. cmd控制台 wrapper | OpenSCManager failed - 拒绝访问。 (0x5)解决

    在windows上安装mycat执行命令时, D:\develop\Mycat\bin>mycat.bat install 返回wrapper | OpenSCManager failed - ...

  5. python简单爬虫(爬取pornhub特定关键词的items图片集)

    请提前搭好梯子,如果没有梯子的话直接403. 1.所用到的包 requests: 和服务器建立连接,请求和接收数据(当然也可以用其他的包,socket之类的,不过requests是最简单好用的) Be ...

  6. IDEA 学习笔记之 Java项目开发

    Java项目开发: 新建模块: 添加JDK: 导入本地Jars: 从远程Maven仓库下载: 创建package: 新建类/接口/枚举等: 字体太小,改字体: Duplicate Scheme 修改编 ...

  7. ASP.NET Web API 2系列(二):灵活多样的路由配置

    1. 导言 路由系统是请求消息进入ASP.NET Web API消息处理管道的第一道屏障,其根本目的在于利用注册的路由对请求的URL进行解析以确定目标HTTPController和Action的名称, ...

  8. 2.1实现简单基础的vector

    2.1实现简单基础的vector 1.设计API 我们参考下C++ <std> 库中的vector, vector中的api很多,所以我们把里面用的频率很高的函数实现; 1.1 new&a ...

  9. 深入理解 web 协议(一)- http 包体传输

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/WlT8070LlrnSODFRDwZsUQ作者:吴越 开坑这个系列的原因,主要是在大前端学习的 ...

  10. drf框架序列化和返序列化

    0903自我总结 drf框架序列化和反序列化 from rest_framework import serializers 一.自己对于序列化和反序列化使用的分类 前后端交互主要有get,post,p ...