lambda 表达式的使用须要借助于 函数式接口, 也就是说只有函数式接口才能够将其用 lambda 表达式进行简化. 函数式接口定义为仅含有一个抽象方法的接口
. 按照这个定义, 一个接口若是声明了两个或两个以上的方法就不叫函数式接口.java
JDK1.8为接口的定义引入了默认方法, 能够用default
关键字在接口中直接定义方法的实现. 若是一个接口存在多个默认方法, 可是仅含有一个抽象方法, 这个接口也符合函数式接口的定义.数组
@FunctionalInterface
注解用于标记该接口是一个函数式接口, 这也是JDK1.8以后新增的. 添加了该注解以后, 编译器会限制接口只容许有一个抽象方法, 不然报错. 建议为函数式接口添加该注解. 在JDK中添加了这个注解的典型接口有 Function, Consumer, Predicate等.app
Consumer Consumer<T> 接收T对象,不返回值框架
Predicate Predicate<T> 接收T对象并返回booleanide
Function Function<T, R> 接收T对象,返回R对象函数
Supplier Supplier<T> 提供T对象(例如工厂),不接收值优化
UnaryOperator UnaryOperator 接收T类型参数, 并返回同一类型的结果. 例如ui
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类型的参数, 并返回同一类型的结果, 例如this
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; }
行为参数化 行为参数化简单的说就是将方法的逻辑以参数的形式传递到方法中, 方法主体仅包含模板类通用代码, 而一些会随着业务场景而变化的逻辑则以参数的形式传递到方法之中, 采用行为参数化可让程序更加的通用, 以应对频繁变动的需求. 例如对于一个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表达式的参数直接当成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 方法
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).
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这个类型是JDK1.8新增的接口方法
boolean tryAdvance(Consumer<? super T> action); Spliterator<T> trySplit();
Spliterator用于对一个数据源进行遍历和分区, 这个数据源能够是一个数组, 一个Collection,一个IO通道, 或者一个生成函数. Spliterator能够单个或成批地处理元素, 也能够将部分元素划分为单独的Spliterator, 不能分区的Spliterator将不能从并行处理中获益. Spliterator用characteristics()方法汇报结构特性. 调用trySplit()的线程能够将返回的Spliterator传递给另外一个线程, 这个线程能够遍历或进一步拆分这个Spliterator.
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表明了一组对象集合, 这些对象就是集合的元素. 一些集合容许重复的元素, 另外一些则不容许. 一些集合是有序的, 另外一些则是无序的. 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() 方法. 具体的实现类可能会对这种状况进行处理, 不过大多数如今的实现并不会这样作.