java8函数式编程之三:集合中新增的方法:forEach
java 1.5的时候增加foreach的增强for循环,然而这里的forEach并不是那个foreach,该处谈论的foreach是集合类的父接口iterable中在jdk1.8新增加的foreach循环函数式方法改迭代方式也叫做内部迭代,内部迭代式通过集合本身的一些接口而不是通过指针去集合中取元素。先执行下面代码:
package com.test.java8; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; public class ForeachTest { public static void main(String[] args) { List<String> testList = new ArrayList<>(); testList.add("a"); testList.add("b"); testList.add("c"); testList.forEach(new Consumer<String>(){ @Override public void accept(String t) { System.out.println(t); } }); } }
毫无疑问该方法会执行打印出集合中所有的元数。跟踪进入forEach函数中,发现该方法是定义在集合类的顶级接口Iterable<T>中:
/** * Performs the given action for each element of the {@code Iterable} * until all elements have been processed or the action throws an * exception. Unless otherwise specified by the implementing class, * actions are performed in the order of iteration (if an iteration order * is specified). Exceptions thrown by the action are relayed to the * caller. * * @implSpec * <p>The default implementation behaves as if: * <pre>{@code * for (T t : this) * action.accept(t); * }</pre> * * @param action The action to be performed for each element * @throws NullPointerException if the specified action is null * @since 1.8 */ default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
注意到该方法前面的关键字:default。default代表了该方法默认情况下可以被子类继承并使用,List接口间接继承自Iterable<T>接口,因此继承了该方法。而且,该方法实现在接口里,这里也是jdk1.8新增的特性,可以在接口里面定义实现方法,凡是在接口里的实现方法,叫做default 方法,这种方法一方面保证了新功能的加入,另一方面不修改该接口的实现类就可以使用该方法。该方法对每一个元素执行给定的action,也就是传值给action,action会一直执行直到所有的元素都执行完或者抛出异常才结束。对于consumer类:
@FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); /** * Returns a composed {@code Consumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }可以看出也是一个函数式接口,代表了操作,该操作接收单独的输入参数,并且该操作不返回结果,不同于其他函数式接口,该方法可以修改接收到的参数。对于accept函数,接收一个值并且不返回值。
在函数式接口定义中:
* <p>Note that instances of functional interfaces can be created with * lambda expressions, method references, or constructor references.可以看出函数调用可以lambda表达式,方法引用,或者构造器引用,所以上面的开始的代码可以替换为:
public class ForeachTest { public static void main(String[] args) { List<String> testList = new ArrayList<>(); testList.add("a"); testList.add("b"); testList.add("c"); // testList.forEach(new Consumer<String>(){ // // @Override // public void accept(String t) { // System.out.println(t); // } // }); // 替换成: testList.forEach( t -> System.out.println(t)); } }
可见,lambda表达式极大简化了匿名内部类的代码。另外,最后一种迭代方式方法引用的使用方法:
public class ForeachTest { public static void main(String[] args) { List<String> testList = new ArrayList<>(); testList.add("a"); testList.add("b"); testList.add("c"); // testList.forEach(new Consumer<String>(){ // // @Override // public void accept(String t) { // System.out.println(t); // } // }); // 替换成: // testList.forEach( t -> System.out.println(t)); // 方法引用 testList.forEach(System.out::println); } }
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。