• java8 的java.util.function包中函数式接口


    Java8 @FunctionalInterface

    java8 的java.util.function包中函数式接口

    java8 Lambda介绍

    包括:

    一. Function 接口的使用

    二. Consumer 接口的使用

    三. Predicate 接口的使用

    四.Supplier接口

    五、Comparator(比较器)

    一. Function 接口的使用

            Java8里关于函数式接口的包是java.util.function,里面全部是函数式接口。主要包含几大类:Function、Predicate、Supplier、Consumer和*Operator(没有Operator接口,只有类似BinaryOperator这样的接口)。后面依次展开详细说明一下。
    如下是Function接口中的定义:
    // T 是传入参数 
    // R 是返回参数

    @FunctionalInterface
    public interface Function<T, R> { R apply(T t); //返回一个组合函数,该函数首先将before函数应用于其输入,然后将此函数应用于结果。如果对其中一个函数的求值引发异常,则会将异常转发给组合函数的调用者。 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } //返回一个组合函数,该函数首先将此函数应用于其输入,然后将after函数应用于结果。如果对其中一个函数的求值引发异常,则会将异常转发给组合函数的调用者。 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } //Returns a function that always returns its input argument.
    static <T> Function<T, T> identity() { return t -> t; } }
    Function接口,其接口声明是一个函数式接口,
    1、其抽象表达函数为apply(T t),函数用途为将参数T传递给一个函数,返回R。即$R=Function(T)$。
    2、其默认实现了3个default方法,分别是compose、andThen和identity,
      对应的函数表达为:
        compose对应V=Function(ParamFunction(T)),体现嵌套关系;
        andThen对应V=ParamFunction(Function(T)),转换了嵌套的顺序;
        还有identity对应了一个传递自身的函数调用对应Function(T)=T
      从这里看出来,compose和andThen对于两个函数f和g来说,f.compose(g)等价于g.andThen(f)。
    示例function-1:
    public class TestFunction {
    
        public static void main(String[] args) {
            Function<Integer, Integer> incr1 = x -> x + 1;
            Function<Integer, Integer> multiply = x -> x * 2;
            
            int x = 2;
            System.out.println("f(x)=x+1,when x=" + x + ",f(x)=" + incr1.apply(x));
            System.out.println("f(x)=x+1,g(x)=2x,when x=" + x + ",f(g(x))=" + incr1.compose(multiply).apply(x));
            System.out.println("f(x)=x+1,g(x)=2x,when x=" + x + ",g(f(x))=" + incr1.andThen(multiply).apply(x));
            System.out.println("compose vs andThen:f(g(x))" + incr1.compose(multiply).apply(x) + "," + multiply.andThen(incr1).apply(x));
    
        }
    }

     运行结果:

    f(x)=x+1,when x=2,f(x)=3
    f(x)=x+1,g(x)=2x,when x=2,f(g(x))=5
    f(x)=x+1,g(x)=2x,when x=2,g(f(x))=6
    compose vs andThen:f(g(x))5,5
    示例function-2:
            如果 字符串为空,显示 "字符串不能为空",如果字符串长度大于3,显示 "字符串过长"。那么按照普通的方式,我就就是两个 if 语句。现在使用Function 接口如下:
    public void test1() {
            String name = "";  
            String name1 = "12345";  
            System.out.println(validInput(name, inputStr -> inputStr.isEmpty() ? "名字不能为空":inputStr));  
            System.out.println(validInput(name1, inputStr -> inputStr.length() > 3 ? "名字过长":inputStr));  
        }  
          
        public static String validInput(String name,Function<String,String> function) {  
            return function.apply(name);  
        }  
    解释:
    1. 定义 validInput 方法,传入 function 接口,然后在该方法中定义 function.apply(name),也就是说,传入一个 name 参数,应用某些规则,返回一个结果,至于是什么规则,先不定义。
    2. 在main 方法中调用 validInput(name,inputStr ...),这里我们定义规则,利用lambda 表达式, 规则是:传入一个 inputStr 字符串,如果为空,返回 xx;否则 返回 xx。

    1.2、 Function高阶函数

    上面只是普通的lambda表达式,其能力有限。我们会希望引入更强大的函数能力——高阶函数,可以定义任意同类计算的函数。jdk还提供了2个参数的Function接口:
    @FunctionalInterface
    public interface BiFunction<T, U, R> {
    
        R apply(T t, U u);
    
        default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t, U u) -> after.apply(apply(t, u));
        }
    }

    比如这个函数定义,参数是z,返回值是一个Function,这个Function本身又接受另一个参数y,返回z+y。于是我们可以根据这个函数,定义任意加法函数:

    由于高阶函数接受一个函数作为参数,结果返回另一个函数,所以是典型的函数到函数的映射。

    BiFunction提供了二元函数的一个接口声明,举例来说:

    其输出结果将是:f(z)=x*y, when x=3,y=5, then f(z)=15。

    二元函数没有compose能力,只是默认实现了andThen。

    有了一元和二元函数,那么可以通过组合扩展出更多的函数可能。

    Function接口相关的接口包括:

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

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

    IntFunction :R apply(int value);只处理int参数的一元函数;

    LongFunction :R apply(long value);只处理long参数的一元函数;

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

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

    ToIntFunction:int applyAsInt(T value);返回int的一元函数;

    ToIntBiFunction:int applyAsInt(T t, U u);返回int的二元函数;

    ToLongFunction:long applyAsLong(T value);返回long的一元函数;

    ToLongBiFunction:long applyAsLong(T t, U u);返回long的二元函数;

    DoubleToIntFunction:int applyAsInt(double value);接受double返回int的一元函数;

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

    IntToDoubleFunction:double applyAsDouble(int value);接受int返回double的一元函数;

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

    LongToDoubleFunction:double applyAsDouble(long value);接受long返回double的一元函数;

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

    1.3、Operator

    Operator其实就是Function,函数有时候也叫作算子。算子在Java8中接口描述更像是函数的补充,和上面的很多类型映射型函数类似。

    算子Operator包括:UnaryOperator和BinaryOperator。分别对应单元算子和二元算子。

    算子的接口声明如下:

    二元算子的声明:

    很明显,算子就是一个针对同类型输入输出的一个映射。在此接口下,只需声明一个泛型参数T即可。对应上面的例子:

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

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

    LongUnaryOperator:long applyAsLong(long operand);

    IntUnaryOperator:int applyAsInt(int operand);

    DoubleUnaryOperator:double applyAsDouble(double operand);

    DoubleBinaryOperator:double applyAsDouble(double left, double right);

    IntBinaryOperator:int applyAsInt(int left, int right);

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

    二、Consumer

    Consumer也是一个Function接口的特殊表达——接受一个泛型参数,不需要返回值的函数接口。接口里面重要方法为:
    /**
         * Performs this operation on the given argument.
         *
         * @param t the input argument
         */
        void accept(T t);
    
    default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    这个接口声明太重要了,对于一些纯粹consume型的函数,没有Consumer的定义真无法被Function家族的函数接口表达。因为Function一定需要一个泛型参数作为返回值类型(当然不排除你使用Function来定义,但是一直返回一个无用的值)。比如下面的例子,如果没有Consumer,类似的行为使用Function表达就一定需要一个返回值。
    public class Test8Consumer {
    
        public static void main(String[] args) {
            Consumer<Integer> consumer = System.out::println;
            consumer.accept(100);
            
            //use function, you always need one return value
            Function<Integer, Integer> function = x -> {
                System.out.println(x);
                return x;
            };
            function.apply(100);
        }
    }

     结果:

    100
    100
    2.2 Consumer 和 Function 的区别
            主要就是 Consumer 接口没有返回值, Function 接口有返回值。
     

    三、 Predicate 接口

    predicate是一个谓词函数,主要作为一个谓词演算推导真假值存在,其意义在于帮助开发一些返回bool值的Function,简单来说就是:判断 输入的对象是否 符合某个条件。本质上也是一个单元函数接口,其抽象方法test接受一个泛型参数T,返回一个boolean值。等价于一个Function的boolean型返回值的子集。
    @FunctionalInterface
    public interface Predicate<T> {
        /**
         * 具体过滤操作 需要被子类实现.
         * 用来处理参数T是否满足要求,可以理解为 条件A
         */
        boolean test(T t);
        /**
         * 调用当前Predicate的test方法之后再去调用other的test方法,相当于进行两次判断
         * 可理解为 条件A && 条件B
         */
        default Predicate<T> and(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) && other.test(t);
        }
        /**
         * 对当前判断进行"!"操作,即取非操作,可理解为 ! 条件A
         */
        default Predicate<T> negate() {
            return (t) -> !test(t);
        }
        /**
         * 对当前判断进行"||"操作,即取或操作,可以理解为 条件A ||条件B
         */
        default Predicate<T> or(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) || other.test(t);
        }
    
        /**
         * 对当前操作进行"="操作,即取等操作,可以理解为 A == B
         */
        static <T> Predicate<T> isEqual(Object targetRef) {
            return (null == targetRef)
                    ? Objects::isNull
                    : object -> targetRef.equals(object);
        }
    }

     其默认方法也封装了and、or和negate逻辑。

    predicate示例1:

    public static void main(String[] args) {
            /**
             * 1、判断数字是否大于7
             */
            //设置一个大于7的过滤条件
            Predicate<Integer> predicate = x -> x > 7;
            System.out.println(predicate.test(10)); //输出 true
            System.out.println(predicate.test(6));  //输出 fasle
             /**
              * 2、大于7并且
              */
            //在上面大于7的条件下,添加是偶数的条件
            predicate = predicate.and(x -> x % 2 == 0);
            System.out.println(predicate.test(6));  //输出 fasle
            System.out.println(predicate.test(12)); //输出 true
            System.out.println(predicate.test(13)); //输出 fasle
            /**
             * 3、add or 简化写法
             */
            predicate = x -> x > 5 && x < 9;
            System.out.println(predicate.test(10)); //输出 false
            System.out.println(predicate.test(6));  //输出 true
        }

    示例predicate2:

    import java.util.function.Predicate;
    
    public class Test3 {
        public static void main(String[] args) {
            String name = "";
            String name1 = "12";
            String name2 = "12345";
    
            System.out.println(validInput3(name, inputStr -> !inputStr.isEmpty() && inputStr.length() <= 3));
            System.out.println(validInput3(name1, inputStr -> !inputStr.isEmpty() && inputStr.length() <= 3));
            System.out.println(validInput3(name2, inputStr -> !inputStr.isEmpty() && inputStr.length() <= 3));
    
        }
    
        public static boolean validInput3(String name, Predicate<String> function) {
            return function.test(name);
        }
    }
    输出为:
    false
    true
    false

    Predicate在Stream中有应用,Stream的filter方法就是接受Predicate作为入参的。这个具体在后面使用Stream的时候再分析深入。

    其他高阶Predicate接口:

    BiPredicate:boolean test(T t, U u);接受两个参数的二元谓词

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

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

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

    四、Supplier(提供者)

    最后说的是一个叫做Supplier的函数接口,其声明如下:

     其简洁的声明,会让人以为不是函数。这个抽象方法的声明,同Consumer相反,是一个只声明了返回值,不需要参数的函数(这还叫函数?)。也就是说Supplier其实表达的不是从一个参数空间到结果空间的映射能力,而是表达一种生成能力,因为我们常见的场景中不止是要consume(Consumer)或者是简单的map(Function),还包括了new这个动作。而Supplier就表达了这种能力。
    比如你要是返回一个常量,那可以使用类似的做法:
    这保证supplier对象输出的一直是1。
    如果是要利用构造函数的能力呢?就可以这样:

    public class Test8Supplier {
        public static void main(String[] args) {
            Supplier<Test8Supplier> anotherSupplier;
            for(int i = 0; i < 10; i++) {
                anotherSupplier = Test8Supplier::new;
                System.out.println(anotherSupplier.get());
            }
        }
    }

    这样的输出可以看到,全部的对象都是new出来的。
    这样的场景在Stream计算中会经常用到,具体在分析Java 8中Stream的时候再深入。
    其他Supplier接口:
    BooleanSupplier:boolean getAsBoolean();返回boolean
    DoubleSupplier:double getAsDouble();返回double
    IntSupplier:int getAsInt();返回int
    LongSupplier:long getAsLong();返回long

    五、Comparator(比较器

    @FunctionalInterface
    public interface Comparator<T>  {
    
        /**
         * 比较方法逻辑
         * @param o1    参数1
         * @param o2    参数2
         * @return      返回值大于0 ---> (o1 > o2)
         *              返回值等于0 ---> (o1 = o2)
         *              返回值小于0 ---> (o1 < o2)
         */
        int compare(T o1, T o2);
    }
    总结:
    常见的几个函数式接口的大概总结如下:

    参考:

    https://blog.csdn.net/lz710117239/article/details/76192629?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-2&spm=1001.2101.3001.4242

     

  • 相关阅读:
    阿里巴巴集团2014年校园招聘系统project师北京笔试题
    HDU 3104 Combination Lock(数学题)
    MYSQL数据迁移
    nginx配置文件中的location中文详解
    使用JQuery解析、处理JSON数据(应用在课程表)
    OpenResty+lua+GraphicsMagick生成缩略图
    解决使用base64解码太慢的问题,原因是根本就不应该使用此方法解决。
    Bringing up interface eth0: Determining if ip address 10.109.67.81 is already in use for device eth0...
    高效访问Internet-启用ISA Server的缓存
    在OpenResty中使用lua-zlib的方法
  • 原文地址:https://www.cnblogs.com/duanxz/p/2593029.html
Copyright © 2020-2023  润新知