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

 

posted on 2012-07-16 09:32  duanxz  阅读(1388)  评论(0编辑  收藏  举报