1. lambda表达式的语法

lambda表达式是一种没有名字的函数,它拥有函数体和参数。

lambda表达式的语法十分简单:参数->主体。通过->来分离参数和主体。

1.1 参数

lambda表达式可以有零个参数,一个参数或多个参数,参数可以指定类型,在编译器可以推导出参数类型的情况下,也可以省略参数类型。

两个参数的例子:

(String first, String second)-> Integer.compare(first.length(), second.length())

0个参数的例子:

() -> { for (int i = 0; i < 1000; i++) doWork(); }

关于省略参数类型,可以参考泛型省略类型来理解。从jdk7开始,泛型可以简化写成如下形式:

Map myMap = new HashMap<>();

编译器会根据变量声明时的泛型类型自动推断出实例化HashMap时的泛型类型。

同样的,如果编译器可以推导出Lambda表达式中参数的类型,也可以将其省略,例如:

Comparatorcomp= (first, second) -> Integer.compare(first.length(), second.length());

上例lambda创建了一个函数式接口Comparator的对象(后文将介绍函数式接口),编译器根据声明,可以推断出first和second的类型为String。此时,参数类型可省略。在只有一个参数,且可推断出其类型的情况下,可以再将括号省略:

EventHandler listener = event ->System.out.println("Thanks for clicking!");

同方法参数一样,表达式参数也可以添加annotations或者final修饰:

(final String name) -> ...

(@NonNull String name) ->

1.2 主体

lambda表达式的主体一定要有返回值。

如果主体只有一句,则可以省略大括号:

Comparator comp = (first, second) -> Integer.compare(first.length(), second.length());

多于一句的情况,需要用{}括上:

(String first, String second) -> {

if (first.length() < second.length()) return -1;

else if (first.length() > second.length()) return 1;

else return 0;

}

主体必须有返回值,只在某些分支上有返回值也是不合法的,例如:

(int x) -> { if (x >= 0) return 1; }

这个例子是不合法的。

2. 函数式接口

只包含一个抽象方法的接口叫做函数式接口。

函数式接口可使用注解@FunctionalInterface标注(不强制,但是如果标注了,编译器就会检查它是否只包含一个抽象方法)

可以通过lambda表达式创建函数式接口的对象,这是lambda表达式在java中做的最重要的事情

在jdk8以前,其实已经存在着一些接口,符合上述函数式接口的定义。

2.1 JDK 8之前已有的函数式接口

java.lang.Runnable

java.util.concurrent.Callable

java.security.PrivilegedAction

java.util.Comparator

java.io.FileFilter

java.nio.file.PathMatcher

java.lang.reflect.InvocationHandler

java.beans.PropertyChangeListener

java.awt.event.ActionListener

javax.swing.event.ChangeListener

在jdk8以前,这些接口的使用方式与其他接口并无不同。

通过两个例子来说明lambda表达式如何创建函数式接口实例

1.创建Runnable函数式接口实例,以启动线程——jdk8以前:

import java.util.*;public classOldStyle {public static voidmain(String[] args) {//启动一个线程

Worker w = newWorker();newThread(w).start();//启动一个线程

new Thread(newRunnable() {

@Overridepublic voidrun() {

System.out.println(Thread.currentThread().getName());

}

}).start();

}

}class Worker implementsRunnable {public voidrun() {

System.out.println(Thread.currentThread().getName());

}

}

运行结果:

Thread-0

Thread-1

从代码角度来看,不管是通过内部类还是通过匿名内部类,启动线程需要编写的代码都较为繁琐,其中,由程序员自定义的仅仅是run方法中的这一句话:

System.out.println(Thread.currentThread().getName());

lambda表达式风格的启动线程:

//启动一个线程

Runnable runner = () ->System.out.println(Thread.currentThread().getName());

runner.run();

第一行实际上创建了一个函数式接口Runnable的实例runner,可以看出,lambda表达式的实体,恰好是run方法的方法体部分。

2.创建Comparator函数式接口实例,实现根据String的长度来排序一个String数组——jdk8以前:

import java.util.*;public classOldStyle {public static voidmain(String[] args) {//排序一个数组

class LengthComparator implements Comparator{public intcompare(String first, String second) {returnInteger.compare(first.length(), second.length());

}

}

String[] strings= "Mary had a little lamb".split(" ");

Arrays.sort(strings,newLengthComparator());

System.out.println(Arrays.toString(strings));

}

}

lambda表达式:

import java.util.*;public classLambdaStyle {public static voidmain(String[] args) {//排序一个数组

String[] strings = "Mary had a little lamb".split(" ");

Arrays.sort(strings, (first, second)->Integer.compare(first.length(), second.length()));

System.out.println(Arrays.toString(strings));

}

}

可以看出,函数式接口通过lambda表达式创建实例,是如此的精简

jdk8的java.util.function包下,又定义了一些函数式接口以及针对基本数据类型的子接口。

Predicate --传入一个参数,返回一个bool结果, 方法为boolean test(T t)

Consumer--传入一个参数,无返回值,纯消费。 方法为void accept(T t)

Function --传入一个参数,返回一个结果,方法为R apply(T t)

Supplier--无参数传入,返回一个结果,方法为T get()

UnaryOperator-- 一元操作符, 继承Function,传入参数的类型和返回类型相同。

BinaryOperator-- 二元操作符, 传入的两个参数的类型和返回类型相同, 继承BiFunction

3. 方法引用

方法引用增强了lambda表达式的可读性

方法表达式的三种主要情况:

类::静态方法

对象::实例方法

类::实例方法

方法引用将会执行该类(对象)的指定静态(实例)方法。

方法引用例1:根据字母顺序(不区分大小写)排序一个字符串数组:

import java.util.*;public classLambdaStyle {public static voidmain(String[] args) {//排序一个数组

String[] strings = "Mary had a little lamb".split(" ");

Arrays.sort(strings, (s1, s2)->{int n1 =s1.length();int n2 =s2.length();int min =Math.min(n1, n2);for (int i = 0; i < min; i++) {char c1 =s1.charAt(i);char c2 =s2.charAt(i);if (c1 !=c2) {

c1=Character.toUpperCase(c1);

c2=Character.toUpperCase(c2);if (c1 !=c2) {

c1=Character.toLowerCase(c1);

c2=Character.toLowerCase(c2);if (c1 !=c2) {//No overflow because of numeric promotion

return c1 -c2;

}

}

}

}return n1 -n2;

});

System.out.println(Arrays.toString(strings));

}

}

上述例子,由于lambda表达式的主体代码较长,导致代码可读性下降,通过方法引用可以解决这个问题

方法引用例2:类::静态方法

import java.util.*;public classLambdaStyle {public static voidmain(String[] args) {//排序一个数组

String[] strings = "Mary had a little lamb".split(" ");

Arrays.sort(strings, LambdaStyle::myCompareToIgnoreCase);

System.out.println(Arrays.toString(strings));

}public static intmyCompareToIgnoreCase(String s1, String s2){int n1 =s1.length();int n2 =s2.length();int min =Math.min(n1, n2);for (int i = 0; i < min; i++) {char c1 =s1.charAt(i);char c2 =s2.charAt(i);if (c1 !=c2) {

c1=Character.toUpperCase(c1);

c2=Character.toUpperCase(c2);if (c1 !=c2) {

c1=Character.toLowerCase(c1);

c2=Character.toLowerCase(c2);if (c1 !=c2) {//No overflow because of numeric promotion

return c1 -c2;

}

}

}

}return n1 -n2;

}

}

将主体代码抽出来写到一个方法中,然后引用这个方法。

方法引用例3:对象::实例方法

import java.util.*;public classLambdaStyle {public static voidmain(String[] args) {//排序一个数组

String[] strings = "Mary had a little lamb".split(" ");

LambdaStyle lambdaStyle= newLambdaStyle();

Arrays.sort(strings, lambdaStyle::myCompareToIgnoreCase);

System.out.println(Arrays.toString(strings));

}public intmyCompareToIgnoreCase(String s1, String s2){int n1 =s1.length();int n2 =s2.length();int min =Math.min(n1, n2);for (int i = 0; i < min; i++) {char c1 =s1.charAt(i);char c2 =s2.charAt(i);if (c1 !=c2) {

c1=Character.toUpperCase(c1);

c2=Character.toUpperCase(c2);if (c1 !=c2) {

c1=Character.toLowerCase(c1);

c2=Character.toLowerCase(c2);if (c1 !=c2) {//No overflow because of numeric promotion

return c1 -c2;

}

}

}

}return n1 -n2;

}

}

对类::实例方法这种情况的方法引用来说,第一个参数会成为执行方法的对象。

通过一个例子来说明。在String类中实际上已经提供了不区分大小写比较字符串的方法:

public int compareToIgnoreCase(String str)

这个方法的用法为:

String s = "jdfjsjfjskd";

String ss= "dskfksdkf";int i = s.compareToIgnoreCase(ss);

System.out.println(i);

方法引用例4:类::实例

import java.util.*;public classLambdaStyle {public static voidmain(String[] args) {//排序一个数组

String[] strings = "Mary had a little lamb".split(" ");

Arrays.sort(strings, String::compareToIgnoreCase);

System.out.println(Arrays.toString(strings));

}

}

分析例4,对于函数式接口Comparator来说,它的抽象方法为:

int compare(T o1, T o2);

这个方法有两个参数,对于例1来说,出现在lambda表达式参数中的s1,s2,实际上就是这两个参数。例2,例3中的方法myCompareToIgnoreCase的参数也是如此。

而对于第三个关于方法引用的例子,String的compareToIgnoreCase方法只有一个参数。这时,第一个参数将会作为执行方法的对象,(s1.compareToIgnoreCase(s2))

另外,也可以通过如下形式方法引用:

this::实例方法

super::实例方法

方法引用例5:

public classSuperTest {public static voidmain(String[] args) {classGreeter {public voidgreet() {

System.out.println("Hello, world!");

}

}class ConcurrentGreeter extendsGreeter {public voidgreet() {

Thread t= new Thread(super::greet);

t.start();

}

}newConcurrentGreeter().greet();

}

}

4. 构造器引用

和方法引用相似,只不过通过如下方式引用:

类::new

构造器引用可以生成一个类的实例

例1

Stream stream = labels.stream().map(Button::new);

Button[] buttons4= stream.toArray(Button[]::new);

5.变量作用域

lambda表达式引用值,而不是变量。

lambda表达式中引用的局部变量必须是:显示声明为final的,或者虽然没有被声明为final,但实际上也算是有效的final的。

在Java中与其相似的是匿名内部类关于局部变量的引用。

例1:匿名内部类引用局部变量——jdk8以前

public classOutter {public static voidmain(String[] args) {final String s1 = "Hello ";newInner() {

@Overridepublic voidprintName(String name) {

System.out.println(s1+name);

}

}.printName("Lucy");

}

}interfaceInner{public voidprintName(String name);

};

如例1所示,在jdk8以前,匿名内部类引用外部类定义的局部变量,则该变量必须是final的。

jdk8将这个条件放宽,匿名内部类也可以访问外部类有效的final局部变量——即这个变量虽然没有显示声明为final,但定义后也没有再发生变化。

例2:匿名内部类引用局部变量——jdk8

public classOutter {public static voidmain(String[] args) {

String s1= "Hello ";newInner() {

@Overridepublic voidprintName(String name) {

System.out.println(s1+name);

}

}.printName("Lucy");

}

}interfaceInner{public voidprintName(String name);

};

匿名内部类引用的外部类变量s1可以不显示定义为final。但是s1必须在初始化后不再改变。

lambda表达式对于引用局部变量的规则同jdk8中的匿名内部类一样:显示声明为final的,或者虽然没有被声明为final,但实际上也算是有效的final的

import java.io.*;import java.nio.charset.*;import java.nio.file.*;import java.util.*;import java.util.stream.*;public classVariableScope {public static voidmain(String[] args) {

repeatMessage("Hello", 100);

}public static void repeatMessage(String text, intcount) {

Runnable r= () ->{for (int i = 0; i < count; i++) {

System.out.println(text);

Thread.yield();

}

};newThread(r).start();

}public static void repeatMessage2(String text, intcount) {

Runnable r= () ->{while (count > 0) {//count--;//Error: Can't mutate captured variable

System.out.println(text);

Thread.yield();

}

};newThread(r).start();

}public static void countMatches(Path dir, String word) throwsIOException {

Path[] files=getDescendants(dir);int matches = 0;for(Path p : files)new Thread(() ->{if(contains(p, word)) {//matches++;//ERROR: Illegal to mutate matches

}

}).start();

}private static intmatches;public static voidcountMatches2(Path dir, String word) {

Path[] files=getDescendants(dir);for(Path p : files)new Thread(() ->{if(contains(p, word)) {

matches++;//CAUTION: Legal to mutate matches, but not threadsafe

}

}).start();

}//Warning: Bad code ahead

public static ListcollectMatches(Path dir, String word) {

Path[] files=getDescendants(dir);

List matches = new ArrayList<>();for(Path p : files)new Thread(() ->{if(contains(p, word)) {

matches.add(p);//CAUTION: Legal to mutate matches, but not threadsafe

}

}).start();returnmatches;

}public staticPath[] getDescendants(Path dir) {try{try (Stream entries =Files.walk(dir)) {return entries.toArray(Path[]::new);

}

}catch(IOException ex) {return new Path[0];

}

}public static booleancontains(Path p, String word) {try{return newString(Files.readAllBytes(p),

StandardCharsets.UTF_8).contains(word);

}catch(IOException ex) {return false;

}

}

}

-----

-----

---

java se 8 for_Java SE 8 for the Really Impatient读书笔记——Java 8 Lambda表达式相关推荐

  1. JAVA高并发程序设计(葛一鸣著)读书笔记

    本文为JAVA高并发程序设计(葛一鸣著)读书笔记.这本书对于刚刚入门的童鞋来讲可能有点深,我推荐可以先看看Java多线程编程核心技术(高洪岩著)一书. 第一章 走入并行世界 什么是同步和异步? 同步就 ...

  2. [读书笔记]java基础与案例详解 主编徐明华

    Java SE(标准版),SUN平台体系中最基础最底层的版本,它是各种应用平台的基础. Java SE包含了桌面应用API和Java EE企业级与Java ME嵌入式. Java是一种计算机编程语言: ...

  3. java写方法用来调用_Java从入门到入土(79)lambda表达式和方法引用

    lambda表达式是Java8引入的新功能.lambda表达式以字面量的形式把少量代码直接写在程序中,从而让 Java 编程更符合函数式风格(Java 实质上是面向对象语言.不过,引入lambda 表 ...

  4. 【Java 进阶】匿名类(代码传递、回调、过滤器)、Lambda表达式(方法引用)、函数式接口(Supplier、Consumer、Predicate、Function)

    匿名类 匿名类(Anonymous Class) 匿名类的使用注意 匿名类 - 代码传递 - 测试代码运行时间的工具类 匿名类 - 回调 - 简易网络请求 匿名类 - 过滤器 - 获取目录下的所有文件 ...

  5. 读书笔记 — Java高并发程序设计 — 第二章 — 基础(上)

    2019独角兽企业重金招聘Python工程师标准>>> 1 有关线程的一些事 线程的所有状态都在Thread中的State枚举中定义: public enum State {/*** ...

  6. java学习笔记20(Lambda表达式、函数式编程、流式计算、练习)

    文章目录 11.3 学习内容 Lambda表达式 Lambda标准格式 格式说明 省略规则 使用前提 函数式接口 预定义的函数式接口 工作内容 任务1 总结&明日计划 11.4 学习内容 流式 ...

  7. 【Java并发编程的艺术】读书笔记——Java并发编程基础

    学习参考资料:<Java并发编程的艺术> 文章目录 1.线程的几种状态 2.如何安全的终止线程 3.线程间通信(重要) 3.1共享内存 3.2消息传递 1.线程的几种状态 线程在运行的生命 ...

  8. OnJava8读书笔记(java编程思想)--集合Collections

    本篇博文参考on Java8中文版编写 本编博文参考java编程思想第四版编写 文章目录 概述 一.泛型和类型安全的集合 二.基本概念 三.添加元素组(Adding Groups of Element ...

  9. 读书笔记——Java虚拟机垃圾收集器与内存分配策略

    本文章已授权微信公众号郭霖(guolin_blog)转载. 本文章讲解的内容是Java虚拟机垃圾收集器与内存分配策略. 概述 说起垃圾收集(Garbage Collection),也就是GC,大部分人 ...

最新文章

  1. kali linux安装wine32,永恒之蓝msf下 ms17_010 (64位kali下安装wine32)
  2. 目标检测——从RCNN到Faster RCNN 串烧
  3. vue完全编程方式与react在书写和运用上的异同
  4. python如何进行格式化输出变量_Python变量格式化输出实现原理解析
  5. 扩增子和宏基因组数据分析流程和可视化方案—刘永鑫(南京,2020年10月27日)
  6. 网页设计中 透明效果的使用技巧
  7. 一篇文章搞定java序列化机制
  8. php键名相加,php二维数组相同键名相加实例
  9. 学习python 基础密码验证
  10. 北京理工大学c语言作业三做一年级算术题,北京理工大学C语言编程题_答案
  11. H5横竖屏的两种解决方法
  12. 博图注册表删除方法_「博图+仿真+授权」西门子软件安装指南及注意事项
  13. 语音转文字的测试用例
  14. Arcgis 地理配准步骤(底图校正)
  15. mysql 1194_打开网页提示mysql发生错误,错误号1194,请问下该怎么解决? 爱问知识人...
  16. 论文阅读—《Fuzzy Reinforcement Learning Algorithm for the Pursuit-Evasion Differential Games 》
  17. Combating the Elsagate Phenomenon: Deep Learning Architectures for Disturbing Cartoons
  18. html六边形空心,六边形空心预制块模具基本知识
  19. 抖音上的python课程_如何用Python抓抖音上的小姐姐
  20. LINK : fatal error LNK1168: cannot open Debug/xxx.exe for writing

热门文章

  1. 海康威视与自己的笔记本连接
  2. 如何在windows xp中实现自动关机
  3. 拉手网@拉手族的团购记
  4. 烧心吃什么马上能缓解11 oracle,烧心吃什么马上能缓解
  5. EPCSTOE半球型电磁炉E0故障维修
  6. Sizes of tensors must match except in dimension 1. Expected size 24 but got size 25 for tensor numbe
  7. 成都大学生接连遭遇购物网“被注册” 疑信息泄露
  8. 告别大学生活,出来闯荡!
  9. PLS系列001 数据预处理
  10. 电脑城特别加强工具盘【2008年春季版】