行为参数化和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) ...
随机推荐
- for + setTimeout
一.背景 最近在翻看以前的老书<node.js开发指南>,恰好碰到 for 循环 + setTimeout 的经典例子,于是重新梳理了思路并记录下. 二.写在前面,setTimeout 和 ...
- UI基础七:给普通其他界面的PRODUCT 添加标准的搜索帮助
在使用的组件中添加组件对象 Outbound Plug中添加外向连接:OP_PRODUCT METHOD op_product. DATA: lv_title TYPE string, lr_cont ...
- CRM WEB UI 04明细界面添加按钮
好了,这个是个人测试玩的,略风骚...请自行鉴阅 1.明细的组件控制器中增加全局控制属性字段: 2.概览页中工具栏相关方法,重定义GET_BUTTONS METHOD IF_BSP_WD_TOOLBA ...
- 51Nod 1001 数组中和等于K的数对
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1001一开始的想法是排序后二分搜索,发现会进行非常多不必要的遍历,十分耗时 ...
- js正则、js全选、反选、全不选、ajax批删
<button onclick="fun1()">全选</button><button onclick="fun2()">全 ...
- C# 3.0 / C# 3.5 扩展方法
概述 扩展方法是一种特殊的静态方法,可以像扩展类型上的实例方法一样进行调用,能向现有类型“添加”方法,而无须创建新的派生类型.重新编译或以其他方式修改原始类型. 扩展方法的定义实现: public s ...
- C# 3.0 / C# 3.5 隐式(推断)类型 var
概述 你可能对隐式类型(或隐式推断类型)这个名称比较陌生,但是 var 这个关键字应该很熟悉. 在 C# 中使用 var 声明一个对象时编译器会自动根据赋值语句推断这个局部变量的类型. 赋值以后,这个 ...
- [洛谷 P3787] 冰精冻西瓜
题目描述 琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地.这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有根树,琪露诺想要把它们冷冻起来慢慢吃. 这些西瓜蔓具有神奇的性质,可以将经过它的 ...
- 青蛙跳N阶(变态跳)
https://www.nowcoder.com/questionTerminal/22243d016f6b47f2a6928b4313c85387 描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级 ...
- mfscli的使用方法(解决mfscgi响应慢的问题)
在moosefs中,mfscgi是一个python写的server程序,其中的数据是调用同样的python工具mfscli实现的. 每当用浏览器打开mfscgi的时候,它要把所有的表数据请求一遍,非常 ...