Java8 Function、Consumer、Supplier
有关JDK8新特性之前还有三篇博客:
这一篇我们来了解JDK8已经定义好的几个函数式接口
一、概述 |
jdk8之后新增的一个重要的包,java,util.function
该包下所有的接口都是函数式接口,按分类主要分为四大接口类型:Function、Consumer、Predicate、Supplier ,有关Predicate这里不再讲解
java.util.Function包如下
二、Consumer |
作用,一听这个名字就知道是消费某个对象,没有返回值
1、源码
@FunctionalInterface public interface Consumer<T> { /** * 抽象方法:传入一个指定泛型的参数,无返回值 */ void accept(T t); /** * 如同方法名字一样andThen,类似一种相加的功能(下面会举例说明) */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
2、使用示例
public static void main(String[] args) { testConsumer(); testAndThen(); } /** * 一个简单的平方计算 */ private static void testConsumer() { //设置好consumer实现方法 Consumer<Integer> square=x-> System.out.println("平方计算:"+x*x); square.accept(2); } /** * 定义3个consumer并按顺序进行调研andThen方法 */ private static void testAndThen() { //当前值 Consumer<Integer> consumer1=x-> System.out.println("当前值:"+x); //相加 Consumer<Integer> consumer2=x-> System.out.println("相加:"+(x+x)); //相乘 Consumer<Integer> consumer3=x-> System.out.println("相乘:"+x*x); //andThen拼接 consumer1.andThen(consumer2).andThen(consumer3).accept(1); }
运行结果:
单这样消费看去并没啥意义,但如果是集合操作就有意义了,所以jdk8的Iterator接口就引入了Consumer
3、JDK8使用
Iterable接口的forEach方法需要传入Consumer,大部分集合类都实现了该接口,用于返回Iterator对象进行迭代
public interface Iterable<T> { //forEach方法传入的就是Consumer default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } }
我们在来看带来的便利
public static void main(String[] args) { //1有个集合,集合里的对象有age List<Student> studentList=new ArrayList<>(); for (Student student : studentList) { student.setAge(1); } //2、通过forEach的Consumer添加 studentList.forEach(x->x.setAge(1)); }
这样一比较代码确实简洁了点,这就是Consumer给我们写代码带来简洁的地方
三、Supplier |
作用:提前定义可能返回的一个指定类型结果,等调用的时候在获取结果
1、源码
@FunctionalInterface public interface Supplier<T> { /** * 只有这一个抽象类 */ T get(); }
源码非常简单
2、JDK8使用
在JDK8中Optional对象有使用到
Optional.orElseGet(Supplier<? extends T>)//当对象为null,就通过传入supplier创建一个返回
我们看下源码:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } }
使用示例
public static void main(String[] args) { Person son = null; //先判断son是否为null,如果为不为null则返回当前对象,如果为null则返回新创建的对象 BrandDTO optional = Optional.ofNullable(son).orElseGet(() -> new Person()); }
这样代码是不是又简单了。有关Optional下面会写一篇博客
四、Function |
作用,实现一个“一元函数”,即传入一个值经过函数的计算返回另外一个值
@FunctionalInterface public interface Function<T, R> { /** * 抽象方法: 根据一个数据类型T加工得到一个数据类型R */ R apply(T t); /** * 组合函数,调用当前function之前调用 */ default <V> java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } /** * 组合函数,调用当前function之后调用 */ default <V> java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } /** * 静态方法,返回与原函数参数一致的结果。x=y */ static <T> java.util.function.Function<T, T> identity() { return t -> t; } }
使用示例
public static void main(String[] args) { applyTest(); andThenTest(); composeTest(); test(); } /** * 1、apply示例 */ private static void applyTest() { //示例一: Function<String,Integer> function=x->Integer.parseInt(x); Integer a=function.apply("100"); System.out.println(a.getClass()); //结果:class java.lang.Integer } /** * 2、andThen示例 */ private static void andThenTest() { //示例2:使用andThen()实现一个函数 y=10x+10; //先执行 10*x Function<Integer,Integer> function=x->10*x; //通过andThen在执行,这里的x就等于上面的10*x的值 function=function.andThen(x->x+10); System.out.println(function.apply(2)); //结果:30 } /** * 3、compose示例 */ private static void composeTest() { //示例3:使用compose()实现一个函数y=(10+x)2; Function<Integer,Integer> function=x->x*2; //先执行x+10在执行(x+10)*2顺序与上面相反 function=function.compose(x->x+10); System.out.println(function.apply(3)); //结果:26 } /** * 4、总和示例 */ private static void test() { //示例4:使用compose()、andThen()实现一个函数 y=(10+x)*2+10; //执行第二步 Function<Integer,Integer> function=x->x*2; //执行第一步 function=function.compose(x->x+10); //执行第三步 function=function.andThen(x->x+10); System.out.println(function.apply(3)); //结果:36 }
3、JDK8使用
有两个地方很常用
1、V HashMap.computeIfAbsent(R,Function<K,V>) //简化代码,如果指定的键尚未与值关联或与null关联,使用函数返回值替换 2、<R>Stream<R> map(Function<?super T,? extends R> mapper);//转换流
computeIfAbsent使用示例
public static void main(String[] args) { Map<String, List<String>> map = new HashMap<>(); List<String> list; //java8之前的写法 list = map.get("key"); if (list==null){ list=new LinkedList<>(); map.put("key",list); } list.add("11"); //使用computeIfAbsent可以这样写,如果key返回不为空,则返回该集合,为空则创建集合返回 list=map.computeIfAbsent("key",k->new ArrayList<>()); list.add("11"); }
stream中map使用示例
public static void main(String[] args) { List<Person> persionList = new ArrayList<Person>(); persionList.add(new Person("张三",28,"女")); persionList.add(new Person("小小",2,"女")); persionList.add(new Person("李四",65,"男")); //只取出该集合中所有姓名组成一个新集合(将person对象转为string对象) List<String> nameList=persionList.stream().map(Person::getName).collect(Collectors.toList()); System.out.println(nameList.toString()); //输出结果:[张三, 小小, 李四] }
代码看上去又整洁了
总结:这些函数式接口作用在我看来,就是定义一个方法,方法中有个参数是函数式接口,这样的话函数的具体实现则由调用者来实现,这就是函数式接口的意义所在
一般我们也会很少去定义一个方法,方法参数包含函数接口,我们更重要的是学会使用JDK8中带有函数式接口参数的方法,来简化我们的代码