函数式接口, Collection等
Lambda
函数式接口
lambda 表达式的使用需要借助于 函数式接口, 也就是说只有函数式接口才可以将其用 lambda 表达式进行简化. 函数式接口定义为仅含有一个抽象方法的接口
. 按照这个定义, 一个接口如果声明了两个或两个以上的方法就不叫函数式接口.
JDK1.8为接口的定义引入了默认方法, 可以用default
关键字在接口中直接定义方法的实现. 如果一个接口存在多个默认方法, 但是仅含有一个抽象方法, 这个接口也符合函数式接口的定义.
@FunctionalInterface
注解用于标记该接口是一个函数式接口, 这也是JDK1.8之后新增的. 添加了该注解之后, 编译器会限制接口只允许有一个抽象方法, 否则报错. 建议为函数式接口添加该注解. 在JDK中添加了这个注解的典型接口有 Function, Consumer, Predicate等.
- 该注解只能标记在”有且仅有一个抽象方法”的接口上
- JDK8接口中的静态方法和默认方法,都不算是抽象方法
- 接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法
- 该注解不是必须的,如果一个接口符合”函数式接口”定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错
- 在一个接口中定义两个自定义的方法,就会产生Invalid ‘@FunctionalInterface’ annotation; FunctionalInterfaceTest is not a functional interface错误.
Consumer Consumer<T> 接收T对象,不返回值
Predicate Predicate<T> 接收T对象并返回boolean
Function Function<T, R> 接收T对象,返回R对象
Supplier Supplier<T> 提供T对象(例如工厂),不接收值
UnaryOperator UnaryOperator 接收T类型参数, 并返回同一类型的结果. 例如
public static void main(String[] args) {
List<Integer> list = Arrays.asList(10,20,30,40,50);
UnaryOperator<Integer> unaryOpt = i->i*i;
unaryOperatorFun(unaryOpt, list).forEach(x->System.out.println(x));
}
private static List<Integer> unaryOperatorFun(UnaryOperator<Integer> unaryOpt, List<Integer> list){
List<Integer> uniList = new ArrayList<>();
list.forEach(i->uniList.add(unaryOpt.apply(i)));
return uniList;
}
BinaryOperator BinaryOperator 接收两个T类型的参数, 并返回同一类型的结果, 例如
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("X", "A");
map.put("Y", "B");
map.put("Z", "C");
BinaryOperator<String> binaryOpt = (s1,s2)-> s1+"-"+s2;
binaryOperatorFun(binaryOpt, map).forEach(x->System.out.println(x));
}
private static List<String> binaryOperatorFun(BinaryOperator<String> binaryOpt, Map<String,String> map){
List<String> biList = new ArrayList<>();
map.forEach((s1,s2)->biList.add(binaryOpt.apply(s1,s2)));
return biList;
}
Lambda
行为参数化 行为参数化简单的说就是将方法的逻辑以参数的形式传递到方法中, 方法主体仅包含模板类通用代码, 而一些会随着业务场景而变化的逻辑则以参数的形式传递到方法之中, 采用行为参数化可以让程序更加的通用, 以应对频繁变更的需求. 例如对于一个Apple对象
public class Apple {
private Color color;
private Float weight;
public Apple() {}
public Apple(Color color, Float weight) {
this.color = color;
this.weight = weight;
}
...
}
最初是需要筛选颜色, 可以使用颜色作为参数
public static List<Apple> filterApplesByCOlor(Lis<Apple> apples, Color color) {
List<Apple> filtered = new ArrayList<>();
for (final Apple apple : apples) {
if (color.equals(apple.getColor())) {
filtered.add(apple);
}
}
return filtered;
}
如果以重量为参数, 也可以仿照上门的格式再写一个方法 如果筛选的条件不止一种, 需要灵活组合, 那就有必要将filter作为一个参数, 将筛选行为抽象化
public interface AppleFilter {
boolean accept(Apple apple);
}
public static class List<Apple> filterApplesByFilter(List<Apple> apples, AppleFilter filter) {
List<Apple> filtered = new ArrayList<Apple>();
for (final Apple apple : apples) {
if (filter.accept(apple)) {
filtered.add(apple);
}
}
return filtered;
}
public static void main(String[] args) {
//...
AppleFilter filter = new AppleFilter() {
@Override
public boolean accept(Apple apple) {
//...
}
}
//...
}
上面的行为参数化方式采用匿名类实现, 可以在具体调用的地方用匿名类指定函数的具体执行逻辑, 但是还不够简洁, 在 JDK1.8中可以通过 lambda 表达式进行简化
List<Apple> filtered = filterApplesByFilter(apples,
(apple) -> Color.RED.equals(apple.getColor()));
Lambda 表达式的定义与形式 Lambda 表达式
- 本质上是一个函数, 虽然它不属于某个特定的类, 但具备参数列表、函数主体、返回类型, 甚至能够抛出异常
- 其次它是匿名的, lambda 表达式没有具体的函数名称
Lambda 表达式可以像参数一样进行传递, 从而简化代码的编写,其格式定义如下
参数列表 -> 表达式参数列表 -> {表达式集合}
注意
- lambda 表达式隐含了 return 关键字, 所以在单个的表达式中, 我们无需显式的写 return语句, 但是当表达式是一个语句集合的时候则需要显式添加 return语句并用花括号{ } 将多个表达式包围起来.
- lambda中, this不是指向lambda表达式产生的那个SAM对象, 而是声明它的外部对象
方法引用
- objectName::instanceMethod
- ClassName::staticMethod
- ClassName::instanceMethod
前两种方式类似, 等同于把lambda表达式的参数直接当成instanceMethod|staticMethod的参数来调用. 比如System.out::println等同于x->System.out.println(x), Math::max等同于(x, y)->Math.max(x,y), 例如 execStrs.forEach(System.out::println).
最后一种方式等同于把lambda表达式的第一个参数当成instanceMethod的目标对象, 其他剩余参数当成该方法的参数. 比如String::toLowerCase等同于x->x.toLowerCase(). 可以这么理解,前两种是将传入对象当参数执行方法, 后一种是调用传入对象的方法.
List<BigDecimal> bdList = new ArrayList<>();
BigDecimal result = bdList.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
上面的代码,
创建一个BigDecimal列表
转换为 Stream<BigDecimal>
调用 reduce 方法
- 提供了一个定义好的加数, 这里是BigDecimal.ZERO
- 使用BinaryOperator<BigDecimal>, 通过方法BigDecimal::add将两个BigDecimal的值相加
List<Invoice> invoiceList = new ArrayList<>();
//populate
Function<Invoice, BigDecimal> totalMapper = invoice -> invoice.getUnit_price().multiply(invoice.getQuantity());
BigDecimal result = invoiceList.stream()
.map(totalMapper)
.reduce(BigDecimal.ZERO, BigDecimal::add);
上面的代码, 使用了一个Function做stream对象的map.
构造器引用 构造器引用语法: ClassName::new 把lambda表达式的参数当成ClassName构造器的参数. 例如BigDecimal::new等同于x->new BigDecimal(x).
Iterable
JDK1.8中, Iterable接口增加了两个带default实现的方法, 一个是
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
另一个是
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
Spliterator
Spliterator这个类型是JDK1.8新增的接口方法
boolean tryAdvance(Consumer<? super T> action);
Spliterator<T> trySplit();
Spliterator用于对一个数据源进行遍历和分区, 这个数据源可以是一个数组, 一个Collection,一个IO通道, 或者一个生成函数. Spliterator可以单个或成批地处理元素, 也可以将部分元素划分为单独的Spliterator, 不能分区的Spliterator将不能从并行处理中获益. Spliterator用characteristics()方法汇报结构特性. 调用trySplit()的线程可以将返回的Spliterator传递给另一个线程, 这个线程可以遍历或进一步拆分这个Spliterator.
Consumer
Consumer是一个操作, 用于接受单个输入并且不返回结果. 在stream里主要是用于forEach内部迭代的时候, 对传入的参数做一系列的业务操作. Consumer有相关的原始类型实现: IntConsumer,LongConsumer,DoubleConsumer, 是Consumer的特例
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
JDK8中有双冒号的用法,就是把方法当做参数传到stream内部,使stream的每个元素都传入到该方法里面执行一下
public class MyTest {
public static void printValur(String str){
System.out.println("print value : "+str);
}
public static void main(String[] args) {
List<String> al = Arrays.asList("a", "b", "c", "d");
al.forEach(AcceptMethod::printValur);
//下面的方法和上面等价的
Consumer<String> methodParam = AcceptMethod::printValur;
al.forEach(x -> methodParam.accept(x));//方法执行accept
}
}
Collection
Collection是集合类型对象的根接口. collection代表了一组对象集合, 这些对象就是集合的元素. 一些集合允许重复的元素, 另一些则不允许. 一些集合是有序的, 另一些则是无序的. JDK并没有提供这个接口的直接实现, 但是提供了其派生接口的实现, 例如Set和List. 这个接口用于传递和操作集合类型的数据并保持最大的通用特性.
打包类型或多集合类型(包含重复对象的无序集合)应该直接实现这个接口.
所有通用的Collection实现类(通常是实现某个派生接口)都应该提高两个标准的构造方法: 一个 void (无参数) 构造方法和一个单参数构造方法, 前者创建一个空集合, 后者创建一个包含一个元素的集合. 实际上, 后者允许用户用任何集合创建包含相同元素的新类型集合. 虽然不能对这种便利进行强制(因为接口不能包含构造方法)但是Java平台上所有的通用Collection实现都遵守这种约定.
在操作集合时, 如果调用了此集合类型不支持的方法, 应当抛出UnsupportedOperationException. 在特殊情况下, 例如调用对集合并不产生影响时, 可以抛出UnsupportedOperationException, 但这不是强制的. 例如在一个不可修改的collection上调用addAll(Collection)方法时, 如果参数的集合为空时, 不强制抛出异常.
有些集合实现会对元素有限制. 例如一些实现禁止null元素, 另一些则对元素的类型有要求. 添加不合法的元素时会抛出异常, 例如NullPointerException 或 ClassCastException. 而查询一个不合法元素时可能会抛出异常, 也可能会返回false; 这些异常在接口的规范中是可选的.
同步的策略由各个集合实现自己来决定. 由于不是强约束, 被其他线程转换的集合上调用任何接口方法都可能导致未定义的行为, 包含直接调用, 传参后调用, 或者用迭代器对集合进行操作.
Collections框架中的许多方法是根据equals方法定义的. 例如, contains(Object o)方法的定义是: "仅当这个集合包含至少一个元素e满足(o==null ? e==null : o.equals(e))时, 返回true." 这个定义不应该被解释为说明使用非null参数调用Collection.contains时将导致o.equals(e)对每一个e元素进行调用. Collection的实现可以自行优化避免使用equals, 例如首先比较两个元素的hash code. (Object.hashCode() 的定义保证了hash code不相等的两个对象肯定不相等.) 更进一步, Collections 框架的不同实现可以自行利用对象方法的特定行为进行优化..
一些操作中, 对直接或间接包含自己的集合进行递归遍历可能会抛出异常, 例如clone(), equals(), hashCode() 和 toString() 方法. 具体的实现类可能会对这种情况进行处理, 不过大多数现在的实现并不会这样做.
函数式接口, Collection等的更多相关文章
- Java 8中一些常用的全新的函数式接口
这一篇属于菜鸟级博客,只是介绍了一些在Java 8中新出现的一些很有用的接口,通过一些简单的例子加以说明,没有深入地阐述. 函数式接口 什么是函数式接口? 函数式接口,@FunctionalInter ...
- java8 Lambda表达式的新手上车指南(1)--基础语法和函数式接口
背景 java9的一再推迟发布,似乎让我们恍然想起离发布java8已经过去了三年之久,java8应该算的上java语言在历代版本中变化最大的一个版本了,最大的新特性应该算得上是增加了lambda表达式 ...
- Effective Java 第三版——44. 优先使用标准的函数式接口
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 乐字节-Java8新特性之函数式接口
上一篇小乐带大家学过 Java8新特性-Lambda表达式,那什么时候可以使用Lambda?通常Lambda表达式是用在函数式接口上使用的.从Java8开始引入了函数式接口,其说明比较简单:函数式接口 ...
- [译]Java8的函数式接口
Java8引入了 java.util.function 包,他包含了函数式接口,具体的描述在以下api说明文档中: 函数式接口为lambda表达式和方法引用提供目标类型.每个函数式接口有一个单独的抽象 ...
- 函数式接口的使用 (Function、Predicate、Supplier、Consumer)
参考:https://blog.csdn.net/jmj18756235518/article/details/81490966 函数式接口 定义:有且只有一个抽象方法的接口 Function< ...
- 函数式接口与Stream流
lambda表达式是jdk8的特性.lambda表达式的准则是:可推断,可省略. 常规代码写一个多线程 public class Main { public static void main(Stri ...
- JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
jdk1.8新特性知识点: Lambda表达式 Stream API 函数式接口 方法引用和构造器调用 接口中的默认方法和静态方法 新时间日期API default Lambda表达式 L ...
- java8新特性学习:函数式接口
本文概要 什么是函数式接口? 如何定义函数式接口? 常用的函数式接口 函数式接口语法注意事项 总结 1. 什么是函数式接口? 函数式接口其实本质上还是一个接口,但是它是一种特殊的接口:SAM类型的接口 ...
随机推荐
- MySQL备份,使用xtrabackup备份全实例数据时,会造成锁等待吗?那么如果使用mysqldump进行备份呢?
一.xtrabackup和mysqldump会造成锁等待吗? xtrabackup会,它在备份时会产生短暂的全局读锁FTWL(flush table with read lock),用于拷贝frm/M ...
- Activity知识点详解
Activity知识点详解 一.什么是Activity 官方解释: The Activity class is a crucial component of an Android app, and t ...
- 持久化JS存储
<script src="../../lib/persist-min.js"></script> //测试一下本地化存储器 var store = new ...
- String,StringBuffer,StringBuilder区别(笔记)
String类被final修饰,创建的对象为不可变对象,属于字符串常量. 而StringBuffer与StringBuilder创建的属于字符串常量. StringBuffer的方法大多用了Synch ...
- sql分割函数
drop function [dbo].[f_split] create function [dbo].[f_split] ( ),--需要分割的字符串(例如:1,2,3,4,5 我|和|你) )-- ...
- Python获取当前脚本文件夹(Script)的绝对路径
Python获取当前脚本绝对路径 Python脚本有一个毛病,当使用相对路径时,被另一个不同目录下的py文件中导入时,会报找不到对应文件的问题.感觉是当前工作目录变成了导入py文件当前目录.如果你有配 ...
- P4560 [IOI2014]Wall 砖墙
题目描述 给定一个长度为 nn且初始值全为 00的序列.你需要支持以下两种操作: Add L, R, hL,R,h:将序列 [L, R][L,R]内所有值小于 hh的元素都赋为 hh,此时不改变高度大 ...
- 检测并修改linux服务器日期
公司的一个应用服务器license到期了,商务上短时间解决不了.只好将服务器的时间调到去年,临时将就一下. 服务器是vmware虚拟机装的centos,日期每隔一段时间会自动同步,百度了好久,也关闭不 ...
- python的any()函数
any()函数的参数是一个可迭代对象,其中的一个元素有一个为真,则any()函数返回真,除非全部为假的时候才返回假. aaa=[,,,] print(any(aaa)) 返回:false
- Access数据库连接封装类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...