• Java8 新特性 函数式接口


    Java Function中的容易被忽略的方法identity()

    1. /*
    2. * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    3. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
    4. */
    5. package java.util.function;
    6. import java.util.Objects;
    7. /**
    8. * Represents a function that accepts one argument and produces a result.
    9. * 表示接受一个参数并产生一个结果的函数。
    10. *
    11. * @param the type of the input to the function
    12. * 参数是函数的输入类型
    13. * @param the type of the result of the function
    14. * 参数是函数的返回类型
    15. * @since 1.8
    16. */
    17. // 注明是函数式接口
    18. @FunctionalInterface
    19. public interface Function {
    20. /**
    21. * Applies this function to the given argument.
    22. * 将此函数应用于给定参数。
    23. * @param t 函数参数
    24. * @return R 函数返回类型
    25. */
    26. R apply(T t);
    27. /**
    28. * Returns a composed function that first applies the {@code before}
    29. * function to its input, and then applies this function to the result.
    30. * If evaluation of either function throws an exception, it is relayed to
    31. * the caller of the composed function.
    32. *
    33. * @param the type of input to the {@code before} function, and to the
    34. * composed function
    35. * @param before the function to apply before this function is applied
    36. * @return a composed function that first applies the {@code before}
    37. * function and then applies this function
    38. * @throws NullPointerException if before is null
    39. *
    40. * @see #andThen(Function)
    41. */
    42. default Function compose(Functionsuper V, ? extends T> before) {
    43. Objects.requireNonNull(before);
    44. return (V v) -> apply(before.apply(v));
    45. }
    46. /**
    47. * Returns a composed function that first applies this function to
    48. * its input, and then applies the {@code after} function to the result.
    49. * If evaluation of either function throws an exception, it is relayed to
    50. * the caller of the composed function.
    51. * 返回一个组合函数,该函数首先将该函数应用于其输入,然后将该函数应用于结果。如果对任一函数的求值抛出异常,则将其中继到组合函数的调用方。
    52. * @param the type of output of the {@code after} function, and of the
    53. * composed function
    54. * @param after the function to apply after this function is applied
    55. * @return a composed function that first applies this function and then
    56. * applies the {@code after} function
    57. * @throws NullPointerException if after is null
    58. *
    59. * @see #compose(Function)
    60. */
    61. default Function andThen(Functionsuper R, ? extends V> after) {
    62. Objects.requireNonNull(after);
    63. return (T t) -> after.apply(apply(t));
    64. }
    65. /**
    66. * Returns a function that always returns its input argument.
    67. * 该方法返回一个函数,该函数返回输入的参数。
    68. * @param the type of the input and output objects to the function
    69. * @return a function that always returns its input argument
    70. */
    71. static Function identity() {
    72. return t -> t;
    73. }
    74. }

    之前只了解Function是函数式接口,支持Lambda表达式,今日由于需要,才了解到Functionidentity()

    Functionidentity()返回t -> T,即本身。
    也就是说identity()可以换为t -> T

    今天的需求大概是:

    1. 已经有了List,利用stream()获取一个键值对Map
    2. // 构造Map键值对,key:Integer, value:IndexEntity
    3. // key为指标实体的id,value为对应的指标实体
    4. Map map = indexEntities.stream().collect(Collectors.toMap(IndexEntity::getId, Function.identity()));

    IndexEntity::getIdJava8新出的方法引用。

    java8 特性 Function.identity()

    Function.identity()是什么?

    1. // 将Stream转换成容器或Map
    2. Stream stream = Stream.of("I""love""you""too");
    3. Map map = stream.collect(Collectors.toMap(Function.identity(), String::length));

    Function是一个接口,那么Function.identity()是什么意思呢?解释如下:

    Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法,identity()就是Function接口的一个静态方法。
    Function.identity()返回一个输出跟输入一样的Lambda表达式对象,等价于形如t -> t形式的Lambda表达式。

    identity() 方法JDK源码如下:

    1. static Function identity() {
    2. return t -> t;
    3. }

    Function.identity()的应用

    下面的代码中,Task::getTitle需要一个task并产生一个仅有一个标题的key。task -> task是一个用来返回自己的lambda表达式,上例中返回一个task。

    1. private static Map taskMap(List tasks) {
    2. return tasks.stream().collect(toMap(Task::getTitle, task -> task));
    3. }

    可以使用Function接口中的默认方法identity来让上面的代码代码变得更简洁明了、传递开发者意图时更加直接,下面是采用identity函数的代码。

    1. import static java.util.function.Function.identity;
    2. private static Map taskMap(List tasks) {
    3. return tasks.stream().collect(toMap(Task::getTitle, identity()));
    4. }

    Function.identity() or t->t?

    1. Arrays.asList("a""b""c")
    2. .stream()
    3. .map(Function.identity()) // <- This,
    4. .map(str -> str) // <- is the same as this.
    5. .collect(Collectors.toMap(
    6. Function.identity(), // <-- And this,
    7. str -> str)); // <-- is the same as this.

    上面的代码中,为什么要使用Function.identity()代替str->str呢?它们有什么区别呢?

    在上面的代码中str -> strFunction.identity()是没什么区别的因为它们都是t->t。但是我们有时候不能使用Function.identity,看下面的例子:

    1. List list = new ArrayList<>();
    2. list.add(1);
    3. list.add(2);

    下面这段代码可以运行成功:

    int[] arrayOK = list.stream().mapToInt(i -> i).toArray();
    

    但是如果你像下面这样写:

    int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();
    

    运行的时候就会错误,因为mapToInt要求的参数是ToIntFunction类型,但是ToIntFunction类型和Function没有关系

    《Java8新特性》之Lambda表达式、函数式接口、方法引用、Optional

    1、Java8 Lambda表达式


    Lambda表达式也称为闭包,它允许我们把函数当作参数一样传递给某个方法,或者把代码本身当作数据处理。

    早期Java开发者只能使用匿名内部类来实现Lambda表达式。

    最简单的可以由逗号分隔的参数列表、->符号、语句块三部分组成。

    例如:

    1. // 例子1
    2. // 参数e的类型是编译器推理出来的
    3. Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
    4. // 例子2
    5. // 当然也可以将执行参数的类型写上
    6. Arrays.asList( "a", "b", "d" ).forEach((String e)-> System.out.println( e ) );
    7. // 例子3
    8. // 当有多个参数时
    9. Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));
    10. // 例子4
    11. // 当Lambda的语句块只有一行时,可以不使用return语句。
    12. Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));

    ps: 切记当有多个参数,或需要指定参数类型的时候,参数列表要加括号。


    2、 函数式接口

    函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

    作用: 这样的接口可以隐式转换为Lambda表达式。

    只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口,进而导致编译失败。为了客服这种问题,并显式说明某个接口是函数式接口,Java8提供了一个特殊的注解**@FunctionalInterface**Java 库中的所有相关接口都已经带有这个注解了。

    1. @FunctionalInterface
    2. interface Addtions {
    3. int test(int a, int b);// 我是核心
    4. default void hello() {
    5. System.out.println("我不会影响到函数式接口的定义");
    6. }
    7. static void hello1(){
    8. System.out.println("我也不会影响到函数式接口的定义");
    9. }
    10. }

    常用的几个接口:

    • java.util.function.Function
      • R apply(T t);
    • java.util.function.Supplier
      • T get();
    • java.util.function.Predicate
      • boolean test(T t);
    • java.util.function.Consumer
      • void accept(T t);
    • java.lang.Runnable
    • java.util.concurrent.Callable
    • java.security.PrivilegedAction
    • java.lang.reflect.InvocationHandler

    写lamdba表达式时会经常用到四个标黑的函数式接口,重点是她们方法的返回值和方法参数。


    3、接口的默认方法和静态方法

    Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下:

    • 默认方法可以被实现类重写Override
    1. class FunctionalInterfaceTest implements Formula{
    2. @Override
    3. public double calculate(int a) {
    4. return 0;
    5. }
    6. // 可以重写sqrt方法。
    7. @Override
    8. public double sqrt(int a) {
    9. return Formula.super.sqrt(a);
    10. }
    11. }
    12. @FunctionalInterface
    13. interface Formula {
    14. double calculate(int a);
    15. // 该方法(默认方法)可以被实现类重写
    16. default double sqrt(int a) {
    17. return Math.sqrt(a);
    18. }
    19. static void hello1(){
    20. System.out.println("我是新来的(JAVA8),我叫静态方法,");
    21. }
    22. }

    4、方法引用

    方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。

    可见使用Lambda表达式的写法和使用方法引用的写法的效果是一样的,但是使用方法引用有时会更加简化代码

    • 构造器引用
      • 类名::new
    • 静态方法引用
      • 类名::静态方法
    • 对象方法引用
      • 类名::方法
      • 当Lambda表达式的参数列表第一个参数为实例方法的调用者,第二个参数(或无参)是实例方法的参数时,可以使用这种方法。
    • 实例方法引用
      • 实例对象::成员方法
      • 要先获取一个实例对象
    1. public class Test {
    2. private String name;
    3. public String getName() {
    4. return this.name;
    5. }
    6. public Test(String name) {
    7. this.name = name;
    8. }
    9. public static String staticMethod(){
    10. return "我是静态方法!";
    11. }
    12. public static void main(String[] args) {
    13. Test test1 = new Test("小明");
    14. // Lambda表达式
    15. Supplier func1 = () -> test1.getName();
    16. System.out.println("Lambda表达式测试:" + func1.get());
    17. // 实例方法引用
    18. Supplier func2 = test1::getName;
    19. System.out.println("方法引用方式测试:" + func2.get());
    20. // 静态方法引用
    21. Supplier func3 = Test::staticMethod;
    22. System.out.println("静态方法引用测试:" + func3.get());
    23. // 构造方法引用(构造器引用)
    24. Function func4 = Test::new;
    25. Test test2 = func4.apply("xxx");
    26. System.out.println("构造方法引用测试:" + test2);
    27. // 对象方法引用
    28. // Test为类名,getName为成员方法。
    29. Function func5 = Test::getName;
    30. System.out.println("对象方法测试引用:" + func5.apply(test1));
    31. }
    32. }

    5、Optional

    Java应用中最常见的bug就是NullPointerException,

    就比如比较两个字符串是否相等

    s1.equals(s2),如果s1==null,那么一运行,console立马就爆红了。

    所以Java8提供了Optional来解决这问题。

    • isPresent(): 如果Optional实例持有一个非空值,方法返回true,否则返回false
    • orElseGet():,Optional实例持有null,则可以接受一个lambda表达式生成的默认值
    • map(): 可以将现有的Opetional实例的值转换成新的值
    • orElse(): Opetional 实例持有null的时候返回传入的默认值, 方法与orElseGet() 方法类似。
    • filter(): 如果optional实例不为null,并且filter中lambda表达式返回true,就返回一个Optional实例;反之返回一个空optional。
      • If a value is present, and the value matches the given predicate,return an {@code Optional} describing the value, otherwise return an empty {@code Optional}.

    1. 当optional实例为null时
    1. Optional< String > fullName = Optional.ofNullable( null );
    2. System.out.println( "Full Name is set? " + fullName.isPresent() );
    3. System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
    4. System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
    5. // 下面为输出结果
    6. Full Name is set? false
    7. Full Name: [none]
    8. Hey Stranger!

      2.当optional实例不为null时

    1. Optional< String > firstName = Optional.of( "Tom" );
    2. System.out.println( "First Name is set? " + firstName.isPresent() );
    3. System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
    4. System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ));
    5. //输出结果
    6. First Name is set? true
    7. First Name: Tom
    8. Hey Tom!

    探索Java8:(一)Stream的使用

    转载

    Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

    Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

    Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

    这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

    元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。


    什么是 Stream?

    Stream(流)是一个来自数据源的元素队列并支持聚合操作

    • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算
    • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
    • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

    和以前的Collection操作不同, Stream操作还有两个基础的特征:

    • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
    • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

    一、流的创建

    • stream() − 为集合创建串行流。
    • parallelStream() − 为集合创建并行流。parallelStream其实就是一个并行执行的流.它通过默认的ForkJoinPool,可能提高你的多线程任务的速度。并行流在遍历时可能是无序的。
    1. public class ParallelStream {
    2. public static void main(String[] args) {
    3. List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    4. numbers.stream().forEach(System.out::print);
    5. }
    6. }

    二、 forEach、map、filter、limit、sorted

    1. numbers.stream().forEach(System.out::print);
    2. numbers.stream().forEach(i->System.out.print(i));

    上述两种方法是等价的。

    forEach

    forEach用来对stream中的数据进行迭代,比如上面创建流的操作就使用了forEach。看会上面的例子后理解forEach不会很难的。需要注意的是,forEach操作是不能改变遍历对象本身的。

    Map

    map 方法用于映射每个元素到对应的结果,多数情况下用来处理数据。下面给出一个让原list个位置元素自增2的代码:

    1. public class MapDemo {
    2. public static void main(String[] args) {
    3. List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    4. List<Integer> outPutList = numbers.stream().map(i -> i + 2).distinct().collect(Collectors.toList());
    5. outPutList.forEach(n->System.out.print(n+" "));
    6. }
    7. }

    filter

    filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

    1. List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    2. // 获取空字符串的数量
    3. int count = strings.stream().filter(string -> string.isEmpty()).count();

    Limit

    limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

    1. Random random = new Random();
    2. random.ints().limit(10).forEach(System.out::println);

    还有一个常用的是配合skip()方法用来进行分页操作。

    1. int pageSize=10;
    2. int currentPage=1;
    3. return pageList.stream()
    4. .skip(pageSize * (currentPage-1))
    5. .limit(pageSize)
    6. .collect(Collectors.toList());

    sorted

    sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

    1. Random random = new Random();
    2. random.ints().limit(10).sorted().forEach(System.out::println);

    Collectors

    Collectors 可用于返回列表或字符串,上面介绍map的例子就用到了Collectors,下面给出菜鸟教程的一个例子:

    1. List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    2. List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
    3. System.out.println("筛选列表: " + filtered);
    4. String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
    5. System.out.println("合并字符串: " + mergedString);

    三、 统计

    顾名思义,统计就是用来统计数据的,一般用于int、double、long等基本类型上。

    1. List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
    2. IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
    3. System.out.println("列表中最大的数 : " + stats.getMax());
    4. System.out.println("列表中最小的数 : " + stats.getMin());
    5. System.out.println("所有数之和 : " + stats.getSum());
    6. System.out.println("平均数 : " + stats.getAverage());

    Stream先介绍到这里,我们Stream代码觉得陌生是因为刚接触声明式编程的风格,下一篇应该会介绍lambda表达式和Optional的用法,我们会更多地用声明式的编程风格。

    探索Java8:(二)Function接口的使用

    转载

    Java8 添加了一个新的特性Function,顾名思义这一定是一个函数式的操作。我们知道Java8的最大特性就是函数式接口。所有标注了@FunctionalInterface注解的接口都是函数式接口,具体来说,所有标注了该注解的接口都将能用在lambda表达式上。

    标注了@FunctionalInterface的接口有很多,但此篇我们主要讲Function,了解了Function其他的操作也就很容易理解了。

    1. @FunctionalInterface
    2. public interface Function<T, R> {
    3. R apply(T t);
    4. /**
    5. * @return a composed function that first applies the {@code before}
    6. * function and then applies this function
    7. */
    8. default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    9. Objects.requireNonNull(before);
    10. return (V v) -> apply(before.apply(v));
    11. }
    12. /**
    13. * @return a composed function that first applies this function and then
    14. * applies the {@code after} function
    15. */
    16. default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    17. Objects.requireNonNull(after);
    18. return (T t) -> after.apply(apply(t));
    19. }
    20. }

    为了方便地阅读源码,我们需要了解一些泛型的知识,如果你对泛型已经很熟悉了,那你可以跳过这段 。

    泛型是JDK1.5引入的特性,通过泛型编程可以使编写的代码被很多不同的类型所共享,这可以很好的提高代码的重用性。因为本篇重点不是介绍泛型,所以我们只关注上述Function源码需要用到的泛型含义。

    1. 泛型类

    泛型类使用来表示该类为泛型类,其内部成员变量和函数的返回值都可以为泛型 ,Function源码的标识为,也就是两个泛型参数,此处不再赘述,具体泛型类可以看网上的文章。

    2. 泛型方法和通配符

    在方法修饰符的后面加一个表明该方法为泛型方法,如Function 的源码里的compose方法的。通配符也很好理解,还是compose的例子,我们可以看到compose的参数为一个Function类型,其中Functin的参数指定了其第一个参数必须是V的父类,第二个参数必须继承T,也就是T的子类。

    源码解析

    1.apply

    讲完了上面这些就可以开始研究源码了。

    首先我们已经知道了Function是一个泛型类,其中定义了两个泛型参数T和R,在Function中,T代表输入参数,R代表返回的结果。也许你很好奇,为什么跟别的java源码不一样,Function 的源码中并没有具体的逻辑呢?

    其实这很容易理解,Function 就是一个函数,其作用类似于数学中函数的定义 ,(x,y)跟的作用几乎一致。

    y=f(x)y=f(x)

    所以Function中没有具体的操作,具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式。

     R apply(T t);
    

    举个例子:

    1. public void test(){
    2. Function<Integer,Integer> test=i->i+1;
    3. test.apply(5);
    4. }
    5. /** print:6*/

    我们用lambda表达式定义了一个行为使得i自增1,我们使用参数5执行apply,最后返回6。这跟我们以前看待Java的眼光已经不同了,在函数式编程之前我们定义一组操作首先想到的是定义一个方法,然后指定传入参数,返回我们需要的结果。函数式编程的思想是先不去考虑具体的行为,而是先去考虑参数,具体的方法我们可以后续再设置。

    再举个例子:

    1. public void test(){
    2. Function<Integer,Integer> test1=i->i+1;
    3. Function<Integer,Integer> test2=i->i*i;
    4. System.out.println(calculate(test1,5));
    5. System.out.println(calculate(test2,5));
    6. }
    7. public static Integer calculate(Function<Integer,Integer> test,Integer number){
    8. return test.apply(number);
    9. }
    10. /** print:6*/
    11. /** print:25*/

    我们通过传入不同的Function,实现了在同一个方法中实现不同的操作。在实际开发中这样可以大大减少很多重复的代码,比如我在实际项目中有个新增用户的功能,但是用户分为VIP和普通用户,且有两种不同的新增逻辑。那么此时我们就可以先写两种不同的逻辑。除此之外,这样还让逻辑与数据分离开来,我们可以实现逻辑的复用

    当然实际开发中的逻辑可能很复杂,比如两个方法F1,F2都需要两个个逻辑AB,但是F1需要A->B,F2方法需要B->A。这样的我们用刚才的方法也可以实现,源码如下:

    1. public void test(){
    2. Function<Integer,Integer> A=i->i+1;
    3. Function<Integer,Integer> B=i->i*i;
    4. System.out.println("F1:"+B.apply(A.apply(5)));
    5. System.out.println("F2:"+A.apply(B.apply(5)));
    6. }
    7. /** F1:36 */
    8. /** F2:26 */

    也很简单呢,但是这还不够复杂,假如我们F1,F2需要四个逻辑ABCD,那我们还这样写就会变得很麻烦了。

    2.compose和andThen

    compose和andThen可以解决我们的问题。先看compose的源码

    1. default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    2. Objects.requireNonNull(before);
    3. return (V v) -> apply(before.apply(v));
    4. }

    compose接收一个Function参数,返回时先用传入的逻辑执行apply,然后使用当前Function的apply。

    1. default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    2. Objects.requireNonNull(after);
    3. return (T t) -> after.apply(apply(t));
    4. }

    andThen跟compose正相反,先执行当前的逻辑,再执行传入的逻辑。

    这样说可能不够直观,我可以换个说法给你看看

    compose等价于B.apply(A.apply(5)),而andThen等价于A.apply(B.apply(5))。

    1. public void test(){
    2. Function<Integer,Integer> A=i->i+1;
    3. Function<Integer,Integer> B=i->i*i;
    4. System.out.println("F1:"+B.apply(A.apply(5)));
    5. System.out.println("F1:"+B.compose(A).apply(5));
    6. System.out.println("F2:"+A.apply(B.apply(5)));
    7. System.out.println("F2:"+B.andThen(A).apply(5));
    8. }
    9. /** F1:36 */
    10. /** F1:36 */
    11. /** F2:26 */
    12. /** F2:26 */

    我们可以看到上述两个方法的返回值都是一个Function,这样我们就可以使用建造者模式的操作来使用。

    B.compose(A).compose(A).andThen(A).apply(5);
    

    这个操作很简单,你可以自己试试。

    探索Java8:(三)Predicate接口的使用

    转载

    上一篇学习了下Function接口的使用,本篇我们学习下另一个实用的函数式接口Predicate。

    Predicate的源码跟Function的很像,我们可以对比这两个来分析下。直接上Predicate的源码:

    1. public interface Predicate<T> {
    2. /**
    3. * Evaluates this predicate on the given argument.
    4. */
    5. boolean test(T t);
    6. /**
    7. * Returns a composed predicate that represents a short-circuiting logical
    8. * AND of this predicate and another. When evaluating the composed
    9. * predicate, if this predicate is {@code false}, then the {@code other}
    10. * predicate is not evaluated.
    11. */
    12. default Predicate<T> and(Predicate<? super T> other) {
    13. Objects.requireNonNull(other);
    14. return (t) -> test(t) && other.test(t);
    15. }
    16. /**
    17. * Returns a predicate that represents the logical negation of this
    18. * predicate.
    19. */
    20. default Predicate<T> negate() {
    21. return (t) -> !test(t);
    22. }
    23. /**
    24. * Returns a composed predicate that represents a short-circuiting logical
    25. * OR of this predicate and another. When evaluating the composed
    26. * predicate, if this predicate is {@code true}, then the {@code other}
    27. * predicate is not evaluated.
    28. */
    29. default Predicate<T> or(Predicate<? super T> other) {
    30. Objects.requireNonNull(other);
    31. return (t) -> test(t) || other.test(t);
    32. }
    33. /**
    34. * Returns a predicate that tests if two arguments are equal according
    35. * to {@link Objects#equals(Object, Object)}.
    36. */
    37. static <T> Predicate<T> isEqual(Object targetRef) {
    38. return (null == targetRef)
    39. ? Objects::isNull
    40. : object -> targetRef.equals(object);
    41. }
    42. }

    Predicate是个断言式接口其参数是,也就是给一个参数T,返回boolean类型的结果。跟Function一样,Predicate的具体实现也是根据传入的lambda表达式来决定的。

    boolean test(T t);
    

    接下来我们看看Predicate默认实现的三个重要方法and,or和negate

    1. default Predicate<T> and(Predicate<? super T> other) {
    2. Objects.requireNonNull(other);
    3. return (t) -> test(t) && other.test(t);
    4. }
    5. default Predicate<T> negate() {
    6. return (t) -> !test(t);
    7. }
    8. default Predicate<T> or(Predicate<? super T> other) {
    9. Objects.requireNonNull(other);
    10. return (t) -> test(t) || other.test(t);
    11. }

    这三个方法对应了java的三个连接符号&&、||和!,基本的使用十分简单,我们给一个例子看看:

    1. int[] numbers= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    2. List<Integer> list=new ArrayList<>();
    3. for(int i:numbers) {
    4. list.add(i);
    5. }
    6. Predicate<Integer> p1=i->i>5;
    7. Predicate<Integer> p2=i->i<20;
    8. Predicate<Integer> p3=i->i%2==0;
    9. List test=list.stream().filter(p1.and(p2).and(p3)).collect(Collectors.toList());
    10. System.out.println(test.toString());
    11. /** print:[6, 8, 10, 12, 14]*/

    我们定义了三个断言p1,p2,p3。现在有一个从1~15的list,我们需要过滤这个list。上述的filter是过滤出所有大于5小于20,并且是偶数的列表。

    假如突然我们的需求变了,我们现在需要过滤出奇数。那么我不可能直接去改Predicate,因为实际项目中这个条件可能在别的地方也要使用。那么此时我只需要更改filter中Predicate的条件。

    1. List test=list.stream().filter(p1.and(p2).and(p3.negate())).collect(Collectors.toList());
    2. /** print:[7, 9, 11, 13, 15]*/

    我们直接对p3这个条件取反就可以实现了。是不是很简单?

    isEqual这个方法的返回类型也是Predicate,所以我们也可以把它作为函数式接口进行使用。我们可以当做==操作符来使用。

    1. List test=list.stream()
    2. .filter(p1.and(p2).and(p3.negate()).and(Predicate.isEqual(7)))
    3. .collect(Collectors.toList());
    4. /** print:[7] */
    5. java8 函数式接口编程:https://blog.csdn.net/qq_28410283/category_7718494.html

    便于检索查找。

  • 相关阅读:
    Qt Creator实例之图标主题
    DevOps CI/CD之一: Jenkins和Github
    Qt 综合练习小项目--反金币(1/2)
    C++---AVL树
    ADS基础教程22 - 有限元电磁仿真(FEM)
    第5套.py
    【django】APPEND_SLASH 路由末尾的斜杠问题
    【从零开始学习 SystemVerilog】2.10、SystemVerilog 数据类型—— Associative Array(关联数组)
    TypeScrippt知识
    java+springboot基于微信小程序的在线办公系统 uniapp 小程序
  • 原文地址:https://blog.csdn.net/qq_40193787/article/details/127629417