行为参数化和Lambda表达式
行为参数化是指拿出一个代码块把他准备好却不执行它。这个代码块以后可以被程序的其他部分调用,意味着你可以推迟这块代码的执行。方法接受多种行为作为参数,并在内部使用来完成不同的行为。行为参数话的好处在于可以把迭代要筛选的集合的逻辑与对集合中的每个元素应用的行为区分开来。
Java的匿名类可以同时声明和实力化一个类。但它往往很笨重,占用了很多空间同时还不易理解。
可以把Lambda表达式看作匿名功能,它基本上是没有声明名称的方法,但和匿名类一样,它也可以作为参数传递给一个方法。Lambda表达式可以简洁地表示可传递的匿名(Lambda不像普通方法那样有一个明确的名称)函数(Lambda函数不属于某个特定的类,但Lambda有参数列表,函数主题,返回类型和异常列表)的一种方式:它没有名称,但它有参数列表,返回类型还可能有一个可以抛出的异常列表。Lambda表达式可以作为参数传递给方法或存储在变量中,并且无需像匿名类那样写很多模板代码。
Lambda表达式有三个部分:参数列表,箭头和Lambda主体。(parameters) -> expression 或 (parameters) -> {statements;}
通常在函数式接口上使用Lambda表达式。函数式接口就是只定义一个抽象方法的接口。
Lambda表达式以內联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例,具体来讲时函数式接口的一个具体实现的实例。函数式接口的抽象方法的签名基本上就是Lambda表达式的签名,这种抽象方法称为函数描述符。()->void代表了参数列表为空且返回void的函数。函数式接口通常带有@FunctionalInterface的注解。它用于表示该接口会被设计为一个函数式接口。若定义了一个不是函数式接口但带有@FunctionalInterface的注解,编译器将报错:Multiple non-overriding abstract methods found in interface XX。
java.util.function包中引入了几个新的函数式接口:
Predicate<T>接口定义了一个名为test的抽象方法,它接受一个T对象,并返回一个boolean。通常用于一个涉及类型T的布尔表达式。
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> p){
List<T> results = new ArrayList<>();
for(T t : list){
if(p.test(t)){
results.add(t);
}
}
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
Consumer<T>定义了一个accpet抽象方法,它接受一个T对象,没有返回。通常用于访问类型T的对象并对其执行某些操作。
@FunctionalInterface
public interface Consumer<T>{
void accept(T t);
}
public static <T> void forEach(List<T> list, Consumer<T> c){
for(T t : list){
c.accpet(t);
}
}
forEach(Arrays.asList(1, 2, 3, 4), (Integer i) -> System.out.println());
Function<T, R>接口定义一个apply方法,接受一个T对象,并返回一个R对象。通常用于将输入对象的信息映射到输出。
@FunctionalInterface
public interfaces Function<T, R>{
R apply(T t);
}
public static <T, R> List<R> map(List<T> list, Function<T, R> f){
List<R> results = new ArrayList<>();
for(T t : list){
results.add(f.apply(t));
}
return results;
}
List<Integer> list = map(Arrays.asList("lambdas", "in", "action"), (String s) -> s.length());
将一个原始类型转换为对应的引用类型的机制称为装箱。将引用类型转换为对应的原始类型称为拆箱。Java有自动装箱机制,但是会影响性能。装箱后的值本质上是把原始类型包裹起来,并保存在堆里。因此装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。
通常,针对专门的输入参数类型的函数式接口的名称要加上对应的原始类型前缀,比如DoublePredicate,IntConsumer等。Function接口还有针对输出参数类型的变种:ToIntFunction<T>, IntToDoubleFunction等。
java8常用函数式接口
函数式接口 函数描述符 原始类型特化
Predicate<T> T -> boolean IntPredicate,LongPredicate,DoublePredicate
Consumer<T> T -> void IntConsumer, LongConsumer, DoubleConsumer
Function<T, R> T -> R IntFunction<R>, IntToDoubleFunction, IntToLongFunction,LongFunction<R>, LongToDoubleFunction, LongToIntFunction, DoubleFunction<R>, ToIntFunction<T>, ToDoubleFunction<T>, ToLongFunction<T>
Supplier<T> () -> T BooleanSupplier, IntSupplier, LongSuppier, DoubleSupplier
UnaryOperator<T> T -> T IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator
BinaryOperator<T> (T, T) -> T IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator
BiPredicate<L, R> (L, R) -> boolean
BiConsumer<T, U> (T, U) -> void ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T>
BiFunction<T, U, R> (T, U) -> R ToIntBiFunction<T, U>, ToLongBiFunction<T, U>, ToDoubleBiFunction<T, U>
任何函数式接口都不允许抛出受检查异常。若需要Lambda表达式来抛出异常,有两种方l法,一种是定义一个自己的函数式接口,并声明受检查异常,另一种是把Lambda放到一个try-catch块中。
Lambda的类型是从使用Lambda的上下文推断出来的。上下文中Lambda表达式需要的类型称为目标类型。若Lambda的主体是语句表达式,它就和一个返回void的函数描述符兼容。
Lambda可以使用自有变量,它们被称为捕获Lambda。Lambda可以没有限制地捕获实例变量和静态变量。但局部变量必须显式的声明为final或事实上就是final。因为Lambda表达式只能捕获指派给它们的局部变量一次,实例变量可以看作捕获最终局部变量this。
闭包是一个函数的实例,且它可以无限制地访问哪个函数的非本地变量。
方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。它的基本思想是若一个Lambda代表的只是直接调用这个方法,最好还是用名称来调用它,而不是去描述如何调用它。方法的引用实际上就是根据已有的方法实现来创建Lambda表达式。使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面。方法的名称不需要括号,因为并么有实际调用这个方法。
方法引用主要有三类
1.指向静态方法的方法引用,如Integer::parseInt
2.指向任意类型实例方法的方法引用,如String::length
3.指向现有对象的实例方法的引用,如obja::methodName
对于构造函数,利用它的名字和关键字new来创建它的一个引用。ClassName::new。若是无参的构造函数,() -> ClassName更适合。
Comparator具有一个叫comparing的静态辅助方法,它接受一个Function来提取Comparable健值,并声称一个Comparator对象。
inventory.sort(comparing( (a) -> a.getWeight()));
把多个简单的Lambda复合成复杂的表达式。如两个谓词之间做个or操作或让另一个函数的结果变为另一个函数的输出。
可以通过reversed间给定的比较器逆序。inventory.sort(comparing((a) -> a.getWeight()).reversed());
thenComparing接受一个函数作为参数,若两个对象用第一个Comparator比较后是一样的,则提供第二Comparator。
inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));
谓语接口包括三个方法:negate,and和or。
negate方法用来返回一个Predicate的非。
Predicate<Apple> notRedApple = redApple.negate();
and和or可以将两个Lambda表达式组合起来。and和or方法是按照在表达式链中的位置,从左到右确定优先级的
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));
Function中有andThen和compose两个默认方法,他们都会返回一个Function实例。
andThen方法会返回一个函数,它先对输入应用过一个给定函数,在对输出应用另一个函数。
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1); // g(f(x))
h = f.compose(g);
result = h.apply(1); // f(g(x));
行为参数化和Lambda表达式的更多相关文章
- 行为参数化与lambda表达式 - 读《Java 8实战》
零. 概述 第一部分:1~3章 主要讲了行为参数化和Lambda表达式 第二部分:4~7章 主要讲了流的应用,包括流与集合差异,流的操作,收集器,注的并行执行 第三部分:8~12章 主要讲了怎样用Ja ...
- Java8新特性之Lambda表达式
lambda表达式是java8给我们带来的几个重量级新特性之一,借用lambda表达式,可以让我们的java程序设计更加简洁.最近新的项目摒弃了1.6的版本,全面基于java8进行开发,本文是java ...
- Java 8新特性(一):Lambda表达式
2014年3月发布的Java 8,有可能是Java版本更新中变化最大的一次.新的Java 8为开发者带来了许多重量级的新特性,包括Lambda表达式,流式数据处理,新的Optional类,新的日期和时 ...
- 一篇文章教会你使用Java8中的Lambda表达式
简介 Java 8为开发者带来了许多重量级的新特性,包括Lambda表达式,流式数据处理,新的Optional类,新的日期和时间API等.这些新特性给Java开发者带来了福音,特别是Lambda表达式 ...
- JAVA8 in Action:行为参数化,匿名类及lambda表达式的初步认知实例整理
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.functio ...
- 如何优雅的将文件转换为字符串(环绕执行模式&行为参数化&函数式接口|Lambda表达式)
首先我们讲几个概念: 环绕执行模式: 简单的讲,就是对于OI,JDBC等类似资源,在用完之后需要关闭的,资源处理时常见的一个模式是打开一个资源,做一些处理,然后关闭资源,这个设置和清理阶段类似,并且会 ...
- Upgrading to Java 8——第一章 Lambda表达式
第一章 Lambda表达式 Lamada 表达式是Java SE 8中最重要的新特性,长期以来被认为是在Java中缺失的特性,它的出现使整个java 语言变得完整.至少到目前,在这节中你将学习到什么是 ...
- LINQ(隐式表达式、lambda 表达式)
.NET 中一项突破性的创新是 LINQ(Language Integrated Query,语言集成查询),这组语言扩展让你能够不必离开舒适的 C# 语言执行查询. LINQ 定义了用于构建查询表达 ...
- Java8学习(3)- Lambda 表达式
猪脚:以下内容参考<Java 8 in Action> 本次学习内容: Lambda 基本模式 环绕执行模式 函数式接口,类型推断 方法引用 Lambda 复合 上一篇Java8学习(2) ...
随机推荐
- 十二、持久层框架(MyBatis)
一.PageHelper分页插件的使用 PageHelper是一款MyBatis的分页插件,只需要简单的配置,然后直接调用方法就可以. 1.配置PageHelper插件 在mybatis-config ...
- iOS runtime实用篇--和常见崩溃say good-bye!
程序崩溃经历 其实在很早之前就想写这篇文章了,一直拖到现在. 程序崩溃经历1 我们公司做的是股票软件,但集成的是第三方的静态库(我们公司和第三方公司合作,他们提供股票的服务,我们付钱).平时开发测试的 ...
- 【转】你必须知道的EF知识和经验
注意:以下内容如果没有特别申明,默认使用的EF6.0版本,code first模式. 推荐MiniProfiler插件 工欲善其事,必先利其器. 我们使用EF和在很大程度提高了开发速度,不过随之带来的 ...
- [转]10+倍性能提升全过程--优酷账号绑定淘宝账号的TPS从500到5400的优化历程
摘要: # 10+倍性能提升全过程--优酷账号绑定淘宝账号的TPS从500到5400的优化历程 ## 背景说明 > 2016年的双11在淘宝上买买买的时候,天猫和优酷土豆一起做了联合促销,在天猫 ...
- java下使用chromedriver获取访问页面状态码
##在使用chromedriver的时候 并没有提供api来获取访问页面的状态码,但是可以打开日志来获取到 LoggingPreferences logPrefs = new LoggingPrefe ...
- C/C++ 全局变量的访问
#include <iostream> using namespace std; ; int main(int argc, char **argv) { ; std::cout <& ...
- 转 Deep Learning for NLP 文章列举
原文链接:http://www.xperseverance.net/blogs/2013/07/2124/ 大部分文章来自: http://www.socher.org/ http://deepl ...
- 学习笔记-AngularJs(二)
在接下来学习angularjs中,我按照的就是之前 学习笔记-AngularJs(一)所讲的目录来搭建一个学习的项目,做一个互联网大佬人物简介的例子,当然也可以使用angualrjs上面提供的官方例子 ...
- [IOS微信] Unicode码 转化为字符串
最近在研究IOS手机备份的数据,里面的微信数据中,每一个微信账号对应一个文件:mmsetting.archive 用来保存此账号的详细信息. 该文件是一个加强版的plist文件(此文件使用的是plis ...
- U深度U盘安装win7系统教程
① 使用u深度u盘启动盘制作工具制作好的启动u盘 点击查看 ==>u深度u盘启动盘制作教程 ② ghost win7系统镜像文件(自可上网自行搜索ghost xp系统) 第一步:下载win7系统 ...