行为参数化和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) ...
随机推荐
- python-Tornado 框架
概述 Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了 ...
- Oracle 11.2.0.4.0 Dataguard部署和日常维护(4)-Datauard Gap事件解决篇
Oracle dataguard主库删除备库需要的归档时,会导致gap事情的产生,或者备库由于网络或物理故障原因,倒是备库远远落后于主库,都会产生gap事件,本例模拟gap事件的产生以及处理. 1. ...
- 判断runtime是否运行在docker中及从docker中获取宿主机的ip信息
1.判断运行时环境是否运行在docker中 参考:How to determine if a process runs inside lxc/Docker? 确定进程是否在LXC/Docker中运行? ...
- MapReduce(三)
MapReduce(三) MapReduce(三): 1.关于倒叙排序前10名 1)TreeMap根据key排序 2)TreeSet排序,传入一个对象,排序按照类中的compareTo方法排序 2.写 ...
- [LeetCode] 231. Power of Two ☆(是否2 的幂)
描述 Given an integer, write a function to determine if it is a power of two. 给定一个整数,编写一个函数来判断它是否是 2 的 ...
- JDK8的ConcurrentHashMap也会造成CPU 100%
转载:不止 JDK7 的 HashMap ,JDK8 的 ConcurrentHashMap 也会造成 CPU 100%?原因与解决~ 现象 大家可能都听过JDK7中的HashMap在多线程环境下可能 ...
- Linux升级内核教程(CentOS7)
1.查看当前内核版本 uname -r 2.通过yum升级内核 通过yum升级内核是最保险的升级方式,因为安装的是操作系统发行厂商验证过兼容性的rpm包,升级风险一般很小. yum install - ...
- npm node sass 安装报错
报错为 不能找到python2.7,记得曾经已经安装过python,结果npm install cnpm install npm install node-sass 各种不行,结果在cmd 输入pyt ...
- mvc4自定义辅助器方法的学习
1.什么是辅助器 它们可以对代码块和标记进行打包,以便能够在mvc框架应用程序中重用.可以理解成编写在页面上的C#代码(方法). 2 .自定义辅助器 创建“基本”的mvc示例,并创建Basic控制器, ...
- 逆袭之旅DAY16.东软实训.Oracle.修改用户
2018-07-12 15:49:51