java se 8 for_Java SE 8 for the Really Impatient读书笔记——Java 8 Lambda表达式
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表达式相关推荐
- JAVA高并发程序设计(葛一鸣著)读书笔记
本文为JAVA高并发程序设计(葛一鸣著)读书笔记.这本书对于刚刚入门的童鞋来讲可能有点深,我推荐可以先看看Java多线程编程核心技术(高洪岩著)一书. 第一章 走入并行世界 什么是同步和异步? 同步就 ...
- [读书笔记]java基础与案例详解 主编徐明华
Java SE(标准版),SUN平台体系中最基础最底层的版本,它是各种应用平台的基础. Java SE包含了桌面应用API和Java EE企业级与Java ME嵌入式. Java是一种计算机编程语言: ...
- java写方法用来调用_Java从入门到入土(79)lambda表达式和方法引用
lambda表达式是Java8引入的新功能.lambda表达式以字面量的形式把少量代码直接写在程序中,从而让 Java 编程更符合函数式风格(Java 实质上是面向对象语言.不过,引入lambda 表 ...
- 【Java 进阶】匿名类(代码传递、回调、过滤器)、Lambda表达式(方法引用)、函数式接口(Supplier、Consumer、Predicate、Function)
匿名类 匿名类(Anonymous Class) 匿名类的使用注意 匿名类 - 代码传递 - 测试代码运行时间的工具类 匿名类 - 回调 - 简易网络请求 匿名类 - 过滤器 - 获取目录下的所有文件 ...
- 读书笔记 — Java高并发程序设计 — 第二章 — 基础(上)
2019独角兽企业重金招聘Python工程师标准>>> 1 有关线程的一些事 线程的所有状态都在Thread中的State枚举中定义: public enum State {/*** ...
- java学习笔记20(Lambda表达式、函数式编程、流式计算、练习)
文章目录 11.3 学习内容 Lambda表达式 Lambda标准格式 格式说明 省略规则 使用前提 函数式接口 预定义的函数式接口 工作内容 任务1 总结&明日计划 11.4 学习内容 流式 ...
- 【Java并发编程的艺术】读书笔记——Java并发编程基础
学习参考资料:<Java并发编程的艺术> 文章目录 1.线程的几种状态 2.如何安全的终止线程 3.线程间通信(重要) 3.1共享内存 3.2消息传递 1.线程的几种状态 线程在运行的生命 ...
- OnJava8读书笔记(java编程思想)--集合Collections
本篇博文参考on Java8中文版编写 本编博文参考java编程思想第四版编写 文章目录 概述 一.泛型和类型安全的集合 二.基本概念 三.添加元素组(Adding Groups of Element ...
- 读书笔记——Java虚拟机垃圾收集器与内存分配策略
本文章已授权微信公众号郭霖(guolin_blog)转载. 本文章讲解的内容是Java虚拟机垃圾收集器与内存分配策略. 概述 说起垃圾收集(Garbage Collection),也就是GC,大部分人 ...
最新文章
- kali linux安装wine32,永恒之蓝msf下 ms17_010 (64位kali下安装wine32)
- 目标检测——从RCNN到Faster RCNN 串烧
- vue完全编程方式与react在书写和运用上的异同
- python如何进行格式化输出变量_Python变量格式化输出实现原理解析
- 扩增子和宏基因组数据分析流程和可视化方案—刘永鑫(南京,2020年10月27日)
- 网页设计中 透明效果的使用技巧
- 一篇文章搞定java序列化机制
- php键名相加,php二维数组相同键名相加实例
- 学习python 基础密码验证
- 北京理工大学c语言作业三做一年级算术题,北京理工大学C语言编程题_答案
- H5横竖屏的两种解决方法
- 博图注册表删除方法_「博图+仿真+授权」西门子软件安装指南及注意事项
- 语音转文字的测试用例
- Arcgis 地理配准步骤(底图校正)
- mysql 1194_打开网页提示mysql发生错误,错误号1194,请问下该怎么解决? 爱问知识人...
- 论文阅读—《Fuzzy Reinforcement Learning Algorithm for the Pursuit-Evasion Differential Games 》
- Combating the Elsagate Phenomenon: Deep Learning Architectures for Disturbing Cartoons
- html六边形空心,六边形空心预制块模具基本知识
- 抖音上的python课程_如何用Python抓抖音上的小姐姐
- LINK : fatal error LNK1168: cannot open Debug/xxx.exe for writing
热门文章
- 海康威视与自己的笔记本连接
- 如何在windows xp中实现自动关机
- 拉手网@拉手族的团购记
- 烧心吃什么马上能缓解11 oracle,烧心吃什么马上能缓解
- EPCSTOE半球型电磁炉E0故障维修
- Sizes of tensors must match except in dimension 1. Expected size 24 but got size 25 for tensor numbe
- 成都大学生接连遭遇购物网“被注册” 疑信息泄露
- 告别大学生活,出来闯荡!
- PLS系列001 数据预处理
- 电脑城特别加强工具盘【2008年春季版】