java 8 学习二(Lambda表达式)
粗略的概括:lambda表达式主要用来实现“函数接口”中“唯一”的抽象方法用的。
他的特殊版有 方法引用,构造函数引用,用对应的接口实例接收即可。
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式 它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
- 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
- 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样, Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
- 简洁——无需像匿名类那样写很多模板代码。
lambda表达式的5种方式
public void process(Runnable r){
r.run();
}
process(() -> System.out.println("This is awesome!!"));
为什么只有在需要函数式接口的时候才可以传递Lambda呢?
函数式接口
函数式接口就是只定义一个抽象方法的接口;哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。
一般来说最好用@FunctionalInterface 注解标注一下。
在哪里可以使用Lambda?
第三个例子的Predicate接口的定义如下:
public interface Predicate<T>{
boolean test (T t);
}
环绕模式没看懂
通用函数式接口
Predicate、 Consumer和Function
- java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate); - java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void) 。
- java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。
泛型的一个坑
1.Java类型要么是引用类型(比如Byte、 Integer、 Object、 List) ,要么是原始类型(比如int、 double、 byte、 char)。但是泛型(比如Consumer<T>中的T)只能绑定到引用类型。
2.原始类型也是可以使用的,但是会在内部被封装为引用类型---作者强调---
这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。
所以针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,接口如下:
总结关于函数式接口和Lambda的使用案例、 Lambda的例子,以及可以使用的函数式接口。
Lambda,还有函数式接口 的异常处理
@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;//接口中抛出异常
}
//接口无法抛出的,try catch 包裹
Function<BufferedReader, String> f = (BufferedReader b) -> {
try {
return b.readLine();
}
catch(IOException e) {
throw new RuntimeException(e);
}
};
困惑
当我们第一次提到Lambda表达式时,说它可以为函数式接口生成一个实例。然而, Lambda表达式本身并不包含它在实现哪个函数式接口的信息。为了全面了解Lambda表达式,你应该知道Lambda的实际类型是什么。
类型检查、类型推断以及限制
有了目标类型的概念,同一个Lambda表达式就可以与不同的函数式接口联系起来,只要它们的抽象方法签名能够兼容。比如,前面提到的Callable和PrivilegedAction,这两个接口都代表着什么也不接受且返回一个泛型T的函数。 因此,下面的赋值是有效的:
Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42; Comparator<Apple> c1 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
ToIntBiFunction<Apple, Apple> c2 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
BiFunction<Apple, Apple, Integer> c3 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
所以需要从赋值的上下文、方法调用的上下文(参数和返回值) 来判断lambda表达式。
类型推断
Comparator<Apple> c =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Comparator<Apple> c =
(a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
//有时候显式写出类型更易读,有时候去掉它们更易读。 这两种都可以编译。
List<Apple> greenApples =
filter(inventory, a -> "green".equals(a.getColor()));
//当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略。
局部变量的限制(懵懂)
下面的代码无法编译
错误: Lambda表达式引用的局部变量必须是最终的( final)或事实上最终的
int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;
原书中是这样描述的
方法引用
方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。
当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面。例如,Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight()的快捷写法。
方法引用主要有三类。
1.指向静态方法的方法引用
(例如Integer的parseInt方法,写作Integer::parseInt)
2.指 向 任 意 类 型 实 例 方 法 的 方 法 引 用
( 例 如 String 的 length 方 法 , 写 作String::length)
3.指向现有对象的实例方法的方法引用
(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)。
将lambda表达式重构为等价的方法引用
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
//等价与
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);
构造函数引用
如果就new 一个对象感觉这样有点啰嗦,如果new多个对象,确实省点代码。
构造函数引用的例子:
---------------------------------------------------------------------------------------------------------------------------------
看个例子:
需求--用不同的排序策略给一个Apple列表排序
//第一版正常java代码:
public class AppleComparator implements Comparator<Apple> {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new AppleComparator()); //inventory是一个List的实例,下同 //第二版 匿名内部类
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
//第三版 lambda表达式
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
或者
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
或者
Comparator具有一个叫作comparing的静态辅助方法,它可以接受一个Function来提取Comparable键值,并生成一个Comparator对象(接口的静态方法)
Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());
现在你可以把代码再改得紧凑一点了:
import static java.util.Comparator.comparing;
inventory.sort(comparing((a) -> a.getWeight())); //第四版(最终版)
import static java.util.Comparator.comparing;
inventory.sort(comparing(Apple::getWeight));
---------------------------------------------------------------------------------------------------------------------------------
复合Lambda表达式
比较器复合 --Comparator.comparing 的内置方法
inventory.sort(comparing(Apple::getWeight) //comparing reversed thenComparing 方法返回仍然是Comparator所以可以继续调用内置方法
.reversed()
.thenComparing(Apple::getCountry));
谓词复合 --Predicate 接口自带的 and or negate isEqual方法
Predicate<Apple> redAndHeavyAppleOrGreen =
redApple.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor()));
函数复合 --Function接口的andThen和compose两个默认方法
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
//先加1在乘2
Function<Integer, Integer> h = f.andThen(g); 数学上会写作g(f(x))
//先乘2在加1
Function<Integer, Integer> h = f.compose(g); 数学上会写作f(g(x))
int result = h.apply(1);
java 8 学习二(Lambda表达式)的更多相关文章
- Java 8学习之Lambda表达式
一.lambda表达式 一个lambda表达式包含三个部分: 一段代码 参数 自由变量的值,这里的自由指的是哪些不是参数并且没有在代码中定义的变量. 示例: public static void re ...
- Java函数式编程和lambda表达式
为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...
- Java8学习笔记----Lambda表达式 (转)
Java8学习笔记----Lambda表达式 天锦 2014-03-24 16:43:30 发表于:ATA之家 本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘.因为本人 ...
- Lambda(二)lambda表达式使用
Lambda(二)lambda表达式使用 Lambda 表达式组成: /* param list arrow lambda body (o1,o2) -> o1.getColor().Compa ...
- JAVA8学习——深入浅出Lambda表达式(学习过程)
JAVA8学习--深入浅出Lambda表达式(学习过程) lambda表达式: 我们为什么要用lambda表达式 在JAVA中,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法. 在 ...
- Java核心技术-接口、lambda表达式与内部类
本章将主要介绍: 接口技术:主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现一个或多个接口. lambda表达式:这是一种表示可以在将来的某个时间点执行的代码块的简洁方法. 内 ...
- Java基础教程:Lambda表达式
Java基础教程:Lambda表达式 本文部分内容引用自OneAPM:http://blog.oneapm.com/apm-tech/226.html 引入Lambda Java 是一流的面向对象语言 ...
- java函数式编程之lambda表达式
作为比较老牌的面向对象的编程语言java,在对函数式编程的支持上一直不温不火. 认为面向对象式编程就应该纯粹的面向对象,于是经常看到这样的写法:如果你想写一个方法,那么就必须把它放到一个类里面,然后n ...
- 最全最强 Java 8 - 函数编程(lambda表达式)
Java 8 - 函数编程(lambda表达式) 我们关心的是如何写出好代码,而不是符合函数编程风格的代码. @pdai Java 8 - 函数编程(lambda表达式) 简介 lambda表达式 分 ...
- Java 8:掌握 Lambda 表达式
本文将介绍 Java 8 新增的 Lambda 表达式,包括 Lambda 表达式的常见用法以及方法引用的用法,并对 Lambda 表达式的原理进行分析,最后对 Lambda 表达式的优缺点进行一个总 ...
随机推荐
- 1 datax 安装和简单使用
DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL.Oracle.SqlServer.Postgre.HDFS.Hive.ADS.HBase.TableStore(O ...
- Hbase源码之 compact源码(二)
compact一中介绍了HBASE compact的调度流程,本篇文章主要介绍实际进行compact的过程.先从上文中的chore中接入,在HRegionserver中的compactChecker ...
- [转帖]linux基础知识大纲
linux基础知识大纲 https://blog.csdn.net/CSDN___LYY/article/details/80810403 1.Linux操作系统概述Linux操作系统的发展过程.创始 ...
- Java中转换为二进制的几种实现
public class HexUtil { private static final String[] DIGITS_UPPER = {"0", "1", & ...
- springboot的pom.xml配置
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven ...
- 操作系统与进程.md
目录 1. 操作系统 1.1 作用 1.2 操作系统的发展 2. 进程的理论 2.1 相关名词 2.2 进程的创建 2.3 进程的状态: 1. 操作系统 管理.控制.协调计算机硬件与软件资源的计算 ...
- v-on 事件触发
1.v-on 绑定事件 2.methods: 事件绑定语法. 3.v-on:click 可以简写成@click 但是在mvc中会有问题 <!DOCTYPE html> <html&g ...
- Loadsh 常用方法总结以及在vue中使用Loadsh
Loadsh 常用方法总结以及在vue中使用Loadsh Lodash 是一个一致性.模块化.高性能的 JavaScript 实用工具库.处理复杂数组,对比等可以直接采用该库,也方便快捷. 官方网站 ...
- Android中H5和Native交互的两种方式
Android中H5和Native交互的两种方式:http://www.jianshu.com/p/bcb5d8582d92 注意事项: 1.android给h5页面注入一个对象(WZApp),这个对 ...
- ES6 Class(类)(九)
一.Class类1.构造函数 constructor(){} 说明:a.如果没有重新定义带参数的构造函数,类会默认为我们提供一个不带参数的隐式构造函数b.在创建类的实例时会自动调用类中的构造函数 2. ...