​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。于是我们可以根据这个函数,定义任意加法函数:

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

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

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

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

其输出结果将是: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。分别对应单元算子和二元算子。

算子的接口声明如下:

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

二元算子的声明:

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

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

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

例子里补充一点的是,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逻辑。

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

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的函数接口,其声明如下:

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

 

 其简洁的声明,会让人以为不是函数。这个抽象方法的声明,同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);
}


 


总结:


常见的几个函数式接口的大概总结如下:


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