【小家java】java8新特性之---函数式接口(Supplier、Consumer、Predicate、Function、UnaryOperator,通往高阶设计的好工具)(下)

简介: 【小家java】java8新特性之---函数式接口(Supplier、Consumer、Predicate、Function、UnaryOperator,通往高阶设计的好工具)(下)

public interface Predicate


断言接口,有点意思了。其默认方法也封装了and、or和negate逻辑 和一个静态方法isEqual。


//and方法接收一个Predicate类型,也就是将传入的条件和当前条件以并且的关系过滤数据。
default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}
//or方法同样接收一个Predicate类型,将传入的条件和当前的条件以或者的关系过滤数据
default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
}
//negate就是将当前条件取反
default Predicate<T> negate() {
    return (t) -> !test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
    return (null == targetRef)
            ? Objects::isNull
            : object -> targetRef.equals(object);
}


看几个案例:


public List<Integer> conditionFilterAnd(List<Integer> list, Predicate<Integer> predicate,Predicate<Integer> predicate2){
    return list.stream().filter(predicate.and(predicate2)).collect(Collectors.toList());
}
public List<Integer> conditionFilterOr(List<Integer> list, Predicate<Integer> predicate,Predicate<Integer> predicate2){
    return list.stream().filter(predicate.or(predicate2)).collect(Collectors.toList());
}
public List<Integer> conditionFilterNegate(List<Integer> list, Predicate<Integer> predicate){
    return list.stream().filter(predicate.negate()).collect(Collectors.toList());
}
//大于5并且是偶数
result = predicateTest.conditionFilterAnd(list, integer -> integer > 5, integer1 -> integer1 % 2 == 0);
result.forEach(System.out::println);//6 8 10
System.out.println("-------");
//大于5或者是偶数
result = predicateTest.conditionFilterOr(list, integer -> integer > 5, integer1 -> integer1 % 2 == 0);
result.forEach(System.out::println);//2 4 6 8 9 10
System.out.println("-------");
//条件取反
result = predicateTest.conditionFilterNegate(list,integer2 -> integer2 > 5);
result.forEach(System.out::println);// 1 2 3 4 5
System.out.println("-------");


最后再来看一下Predicate接口中的唯一一个静态方法(小纵范围使用):


isEqual方法返回类型也是Predicate,也就是说通过isEqual方法得到的也是一个用来进行条件判断的函数式接口实例。而返回的这个函数式接口实例是通过传入的targetRef的equals方法进行判断的。我们看一下具体


public static void main(String[] args) {
        System.out.println(Predicate.isEqual("test").test("test")); //true
        System.out.println(Predicate.isEqual(null).test("test")); //false
        System.out.println(Predicate.isEqual(null).test(null)); //true
        System.out.println(Predicate.isEqual(1).test(new Integer(1))); //true
        //注意 这里是false的
        System.out.println(Predicate.isEqual(new Long(1)).test(new Integer(1))); //false
    }


其他Predicate扩展接口:


BiPredicate:boolean test(T t, U u);接受两个参数的,判断返回bool


DoublePredicate:boolean test(double value);入参为double的谓词函数


IntPredicate:boolean test(int value);入参为int的谓词函数


LongPredicate:boolean test(long value);入参为long的谓词函数


public interface Function


这个接口非常非常总要。是很上层的一个抽象。除了一个抽象方法apply外,其默认实现了3个default方法,分别是compose、andThen和identity。


  default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }


compose 和 andThen 的不同之处是函数执行的顺序不同。andThen就是按照正常思维:先执行调用者,再执行入参的。然后compose 是反着来的,这点需要注意。


看看唯一的一个静态方法identity:

static <T> Function<T, T> identity() {
        return t -> t;
    }

我们会发现,identity啥都没做,只是返回了一个Function方法,并且是两个泛型都一样的方法,意义着实不是太大。下面看一个复杂点的例子,各位感受一下:


 public static void main(String[] args) {
        Function<Integer, Integer> times2 = i -> i * 2; //加倍函数
        Function<Integer, Integer> squared = i -> i * i; //平方函数
        System.out.println(times2.apply(4)); //8
        System.out.println(squared.apply(4)); //16
        System.out.println(times2.compose(squared).apply(4));  //32   先4×4然后16×2, 先执行参数,再执行调用者
        System.out.println(times2.andThen(squared).apply(4));  //64   先4×2,然后8×8, 先执行调用者,再执行参数
        //看看这个例子Function.identity()构建出一个恒等式函数而已,方便方法的连缀 这就是它的唯一优点
        System.out.println(Function.identity().compose(squared).apply(4));   //16 先执行4*4,再执行identity 值不变
    }


由Function,可以扩展出高阶函数。如泛型中有个类型还是Function,这种需要还是经常有的,所以BiFunction提供了二元函数的一个接口声明


  public static void main(String[] args) {
        BiFunction<Integer, Integer, Integer> biFunction = (x, y) -> x + y;
        System.out.println(biFunction.apply(4, 5)); //9
        System.out.println(biFunction.andThen(x -> x + 10).apply(4, 5)); //19
    }


二元函数没有compose能力,只是默认实现了andThen。有了一元和二元函数,那么可以通过组合扩展出更多的函数可能。


Function相关扩展接口:


BiFunction :R apply(T t, U u);接受两个参数,返回一个值,代表一个二元函数;


DoubleFunction :R apply(double value);只处理double类型的一元函数;


ToDoubleFunction:double applyAsDouble(T value);返回double的一元函数;


ToDoubleBiFunction:double applyAsDouble(T t, U u);返回double的二元函数;


IntToLongFunction:long applyAsLong(int value);接受int返回long的一元函数;


里面有很多关于int、long、double的一元二元函数,这里就不一一例举了。


Operator


Operator其实就是Function,函数有时候也叫作算子。算子在Java8中接口描述更像是函数的补充,和上面的很多类型映射型函数类似。它包含UnaryOperator和BinaryOperator。分别对应单元算子和二元算子。


UnaryOperator

 @FunctionalInterface
    public interface UnaryOperator<T> extends Function<T, T> {
        static <T> java.util.function.UnaryOperator<T> identity() {
            return t -> t;
        }
    }
  @FunctionalInterface
    public interface BinaryOperator<T> extends BiFunction<T,T,T> {
        public static <T> java.util.function.BinaryOperator<T> minBy(Comparator<? super T> comparator) {
            Objects.requireNonNull(comparator);
            return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
        }
        public static <T> java.util.function.BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
            Objects.requireNonNull(comparator);
            return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
        }
    }

很明显,算子就是一个针对同类型输入输出的一个映射。在此接口下,只需声明一个泛型参数T即可。直接上例子,就非常清楚使用场景了:


  public static void main(String[] args) {
        UnaryOperator<Integer> unaryOperator = x -> x + 10;
        BinaryOperator<Integer> binaryOperator = (x, y) -> x + y;
        System.out.println(unaryOperator.apply(10)); //20
        System.out.println(binaryOperator.apply(5, 10)); //15
        //继续看看BinaryOperator提供的两个静态方法   也挺好用的
        BinaryOperator<Integer> min = BinaryOperator.minBy(Integer::compare);
        BinaryOperator<Integer> max = BinaryOperator.maxBy(Integer::compareTo);
        System.out.println(min.apply(10, 20)); //10
        System.out.println(max.apply(10, 20)); //20
    }


BinaryOperator提供了两个默认的static快捷实现,帮助实现二元函数min(x,y)和max(x,y),使用时注意的是排序器可别传反了:)


提示一个小点:compareTo是Integer的实例方法,而compare是静态方法。其实1.8之后,Interger等都提供了min、max、sum等静态方法


其他的Operator接口:(不解释了)


   LongUnaryOperator:long applyAsLong(long operand);


   LongBinaryOperator:long applyAsLong(long left, long right);

    。。。省略不写了


最后


我们会发现,JDK的设计还是很有规律的。每个函数式接口对基本数据类型的中的int、long、double都提供了对应的扩展接口。可我们是否想过?为什么别的数据基本类型没有对应接口呢?这个我在Stream那一章有说明原因,各位看官可跳转到那里观看


相关文章
|
7月前
|
人工智能 缓存 监控
使用LangChain4j构建Java AI智能体:让大模型学会使用工具
AI智能体是大模型技术的重要演进方向,它使模型能够主动使用工具、与环境交互,以完成复杂任务。本文详细介绍如何在Java应用中,借助LangChain4j框架构建一个具备工具使用能力的AI智能体。我们将创建一个能够进行数学计算和实时信息查询的智能体,涵盖工具定义、智能体组装、记忆管理以及Spring Boot集成等关键步骤,并展示如何通过简单的对话界面与智能体交互。
2919 1
|
8月前
|
数据采集 JSON Java
Java爬虫获取1688店铺所有商品接口数据实战指南
本文介绍如何使用Java爬虫技术高效获取1688店铺商品信息,涵盖环境搭建、API调用、签名生成及数据抓取全流程,并附完整代码示例,助力市场分析与选品决策。
|
7月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
232 11
|
6月前
|
人工智能 监控 Java
Java与AI智能体:构建自主决策与工具调用的智能系统
随着AI智能体技术的快速发展,构建能够自主理解任务、制定计划并执行复杂操作的智能系统已成为新的技术前沿。本文深入探讨如何在Java生态中构建具备工具调用、记忆管理和自主决策能力的AI智能体系统。我们将完整展示从智能体架构设计、工具生态系统、记忆机制到多智能体协作的全流程,为Java开发者提供构建下一代自主智能系统的完整技术方案。
864 4
|
7月前
|
人工智能 Java API
Java AI智能体实战:使用LangChain4j构建能使用工具的AI助手
随着AI技术的发展,AI智能体(Agent)能够通过使用工具来执行复杂任务,从而大幅扩展其能力边界。本文介绍如何在Java中使用LangChain4j框架构建一个能够使用外部工具的AI智能体。我们将通过一个具体示例——一个能获取天气信息和执行数学计算的AI助手,详细讲解如何定义工具、创建智能体并处理执行流程。本文包含完整的代码示例和架构说明,帮助Java开发者快速上手AI智能体的开发。
2815 8
|
6月前
|
Java Go 开发工具
【Java】(9)抽象类、接口、内部的运用与作用分析,枚举类型的使用
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接 口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类abstract static不能同时修饰一个方法。
299 1
|
8月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
850 23
|
8月前
|
安全 Java 开发者
Java集合框架:详解Deque接口的栈操作方法全集
理解和掌握这些方法对于实现像浏览器后退功能这样的栈操作来说至关重要,它们能够帮助开发者编写既高效又稳定的应用程序。此外,在多线程环境中想保证线程安全,可以考虑使用ConcurrentLinkedDeque,它是Deque的线程安全版本,尽管它并未直接实现栈操作的方法,但是Deque的接口方法可以相对应地使用。
457 12
|
8月前
|
存储 安全 Java
Java集合框架(一):List接口及其实现类剖析
本文深入解析Java中List集合的实现原理,涵盖ArrayList的动态数组机制、LinkedList的链表结构、Vector与Stack的线程安全性及其不推荐使用的原因,对比了不同实现的性能与适用场景,帮助开发者根据实际需求选择合适的List实现。

热门文章

最新文章

下一篇
开通oss服务