函数式接口就是只定义一个抽象方法的接口。对于所有的函数式接口,我们都可以为它创建一个 Lambda 表达式。
函数式接口
在此简单介绍一下函数式接口。
什么是函数式接口
函数式接口就是只定义一个抽象方法的接口。由于 JDK8 开始提供 default
关键字,因此函数式接口并不仅仅只能定义一个方法,哪怕一个接口有很多默认方法和静态方法,只要只有一个抽象方法,它就是函数式接口。
函数式接口的作用是什么
函数式接口的作用在于,允许 Lambda 表达式以内联的形式作为函数式接口的实现,并把整个表达式作为它自身的实例。也就是说,Lambda 表达式可以作用在任何以函数式接口为方法参数的地方。
例如,Runnable 就是一个函数式接口,因为它里面只有 void run()
这一个抽象方法,因此可以将 Lambda 表达式作用在以 Runnable 为参数的地方。由于 Thread 的构造方法其中一个是 Thread(Runnable runnable)
,所以可以这样:
newThread(()->System.out.println("HELLOWORLD")).start();
函数描述符
使用一个 Lambda 表达式作为函数式接口的实现时,函数式接口的抽象方法的签名就是 Lambda 表达式的签名。这个抽象方法就叫做函数描述符。
方法签名就是 java 中的方法签名的概念,用于判断同一个类中方法的唯一性,由方法名称、方法参数类型和方法参数数量构成。而对于函数式接口而言,由于规定只能拥有一个抽象方法,以此来表示 Lambda 表达式,因此方法名称这一属性忽略。
注意将函数描述符与方法描述符做区分,方法描述符是 JVM 里的概念,用于描述一个方法的属性,包括方法的参数数量、参数类型和返回值。如
(Ljava/lang/StringI)V
为什么要创造函数式接口?
Java 的设计者当初也曾想过给 Java 添加“函数类型”用于传递方法引用和 Lambda 表达式,但是这样的代价太高了,由于 Java 工程师都熟悉接口这一工具,因此使用接口来传递 Lambda 和方法引用。用于传递 Lambda 的接口就称为函数式接口。
@FunctionalInterface
函数式接口的标识是 @FunctionalInterface
,它标注在接口上面,用于表示这是一个函数式接口。但实际上只要保证只有一个抽象方法,接口就是函数式接口,就可以代表一个 Lambda,就算没有添加这个注解也一样能工作。
所以这个注解的最大意义是用来告知程序员和告知编译器。当程序员看到这个注解就会意识到这是个函数式接口。当编译器识别到这个注解,就会验证接口内是否只有一个抽象方法,如果不是,会报告错误并拒绝编译:
或者
纯粹的函数式接口
函数式编程的思想是“无副作用”和“不变性”。如果一个方法既不修改输入参数的状态,也不修改其所属对象的状态,也不修改程序全局变量的状态,那么这个方法就是存粹的,或者无副作用的。
存粹的函数是函数式编程的追求和指导理念,对于一个存粹的函数,任何对它的使用都是安全的,无论是同步还是并行状态,因为它不可能去改变任何其他对象的状态。
因此,如果一个函数式接口是存粹的函数式接口,那么我们在使用时,可以不考虑任何线程安全、变量逃逸的问题,可以以最安心的方式进行编程。
抛出异常也是对调用者状态的改变,因此一个会抛出异常的函数也不是存粹的函数。
但这只是指导思想,不是一切的准则,因为 Java 那“一切皆对象”的思想在大部分情况下是和“存粹的函数”相冲突的。我们的目的是善用这二者,让我们可以使用更完美的编程策略。而不是为了 A 而否定 B。
Java 中提供的函数式接口
Java 基本上为你能想到的 Lambda 签名的各种形式都提供了默认的函数式接口了,在此进行记录和列出,方便查阅。
Java 提供的函数式接口都在 java.util.function
包中。每个接口的抽象函数都会描述它的 Lambda 签名,但这种描述是非正式的,请注意。你想怎么描述都行,只要包含参数列表和返回值即可。
代码块 Runnable
Runnable 表示“可运行的”,该接口用于表示一个无参无返回值的函数。
void run()
无输入,无输出,Lambda 签名为 () -> {...}
,单纯地执行函数中的代码。
publicinterfaceRunnable{publicabstractvoidrun();}
使用:
//Thread::Thread(Runnabletarget)newThread(()->System.out.println("HELLOWORLD")).start();
谓词 Predicate
一元谓词
Predicate 是“谓语”、“谓词”的意思。谓词表示用于判断,可以类比语法中的谓语,如 is
、is not
、是
和 不是
。该接口用于表示一个对参数进行判断的函数。
boolean test(T t)
输入 T 对象,输出布尔值,Lambda 签名为 t -> {...return boolean}
,若 t 满足条件就返回 true,否则返回 false。
publicinterfacePredicate<T>{booleantest(Tt);//创建Predicate函数链,相当于与另一个Predicate进行与运算defaultPredicate<T>and(Predicate<?superT>other){}//创建相反逻辑的PredicatedefaultPredicate<T>negate(){}//创建Predicate函数链,相当于与另一个Predicate进行或运算defaultPredicate<T>or(Predicate<?superT>other){}/***创造一个Predicate函数,是一个全等函数,该函数用于判断输入参数是否等于targetRef**@paramtargetRef全等函数的判断目标*@param<T>任意类型*@return一个Equal比较函数*/static<T>Predicate<T>isEqual(ObjecttargetRef){return(null==targetRef)?Objects::isNull:object->targetRef.equals(object);}}
使用:
//ArrayList<E>::removeIf(Predicate<?superE>filter)arrayList.removeIf(str->str.length()>42);Predicate<Object>equalWithHelloWorld=Predicate.isEqual("HELLOWORLD");arrayList.removeIf(equalWithHelloWorld);
二元谓词
BiPredicate 也是谓词函数,不过可以接受两个操作数。名称中的 Bi
是“双”的意思,对于函数而言就是“二元”。该接口用于表示一个对两个参数进行合并判断的函数。
boolean test(T t, U u)
输入 T 对象和 U 对象,输出布尔值,Lambda 签名为 (t, u) -> {...return boolean}
,若 t 和 u 满足条件就返回 true,否则返回 false。
publicinterfaceBiPredicate<T,U>{booleantest(Tt,Uu);//创建BiPredicate函数链,相当于另一个BiPredicate进行与运算defaultBiPredicate<T,U>and(BiPredicate<?superT,?superU>other){}//创建相反逻辑的BiPredicatedefaultBiPredicate<T,U>negate(){}//创建BiPredicate函数链,相当于另一个BiPredicate进行或运算defaultBiPredicate<T,U>or(BiPredicate<?superT,?superU>other){}}
使用:
BiPredicate<String,String>biPredicate=String::equals;biPredicate.test("HELLO","WORLD");
特化谓词
DoublePredicate boolean test(double value)
对 double 数据进行判断的 Predicate
IntPredicate boolean test(int value)
对 int 数据进行判断的 Predicate
LongPredicate boolean test(long value)
对 long 数据进行判断的 Predicate
每个特化的 Predicate 都有 and
、or
和 negate
这三个构造函数链的默认方法。
消费者 Consumer
一元消费者
Consumer 表示“消费者”,该接口用于表示一个对参数进行处理的函数。
void accept(T t)
输入 T 对象,无输出,Lambda 签名为 t -> {...}
,用于对 t 进行处理,当然也可以不进行任何操作。
publicinterfaceConsumer<T>{voidaccept(Tt);//创建Consumer函数链,在当前Consumer处理结束后继续对t进行处理。defaultConsumer<T>andThen(Consumer<?superT>after){}}
使用:
//ArrayList<E>::forEach(Consumer<?superE>action)arrayList.forEach(str->System.out.println(str));arrayList.forEach(System.out::println);
二元消费者
BiConsumer 也是消费者,不过作用于两个操作数。该接口用于表示一个对两个输入参数进行处理的函数。
void accept(T t, U u)
输入 T 对象和 U 对象,无输出,Lambda 签名为 (t, u) -> {...}
,用于处理 t 和 u。
publicinterfaceBiConsumer<T,U>{voidaccept(Tt,Uu);//创建BiConsumer函数链,在当前BiConsumer处理结束后继续对t和u进行处理。defaultBiConsumer<T,U>andThen(BiConsumer<?superT,?superU>after){}}
使用:
publicinterfaceRunnable{publicabstractvoidrun();}0
特化消费者
DoubleConsumer void accept(double value)
专门消费 double 类型的消费者
IntConsumer void accept(int value)
专门消费 int 类型的消费者
LongConsumer void accept(long value)
专门消费 long 类型的消费者
ObjDoubleConsumer void accept(T t, double value)
专门消费普通对象和 double 类型的二元消费者
ObjIntConsumer void accept(T t, int value)
专门消费普通对象和 int 类型的二元消费者
ObjLongConsumer void accept(T t, long value)
专门消费普通对象和 long 类型的二元消费者
函数 Function
函数的特点:给定一个输入数据集,会输出运算结果,有输入有输出就是函数。
一元函数
Function 就是函数,但在这里表示的是最普通的一元函数,该接口用于表示一个将输入参数处理并返回处理结果的函数。
R apply(T t)
输入 T 类型对象,输出 R 类型对象,Lambda 签名为 t -> {...return r}
,用于将 t 转化为 R 类型数据。
publicinterfaceRunnable{publicabstractvoidrun();}1
使用:
publicinterfaceRunnable{publicabstractvoidrun();}2
二元函数
BiFunction 用于表示一个对两个输入参数进行处理,并返回处理结果的函数。
R apply(T t, U u)
输入 T 对象和 U 对象,输出 R 类型对象,Lambda 签名为 (t, u) -> {...return r}
,用于将 t 和 u 转化为 R 类型数据。
publicinterfaceRunnable{publicabstractvoidrun();}3
使用:
publicinterfaceRunnable{publicabstractvoidrun();}4
特化函数
DoubleFunction R apply(double value)
专门处理 double 数据并输出 R 类型数据的函数。
DoubleToIntFunction int applyAsInt(double value)
专门处理 double 数据并输出 int 类型数据的函数。
DoubleToLongFunction long applyAsLong(double value)
专门处理 double 数据并输出 long 类型数据的函数。
IntFunction R apply(int value)
专门处理 int 数据并输出 R 类型数据的函数。
IntToDoubleFunction double applyAsDouble(int value)
专门处理 int 数据并输出 double 类型数据的函数。
IntToLongFunction long applyAsLong(int value)
专门处理 int 数据并输出 long 类型数据的函数。
LongFunction R apply(long value)
专门处理 long 数据并输出 R 类型数据的函数。
LongToDoubleFunction double applyAsDouble(long value)
专门处理 long 数据并输出 double 类型数据的函数。
LongToIntFunction int applyAsInt(long value)
专门处理 long 数据并输出 int 类型数据的函数。
ToDoubleFunction double applyAsDouble(T t)
专门处理 T 数据并输出 double 类型数据的函数。
ToIntFunction int applyAsInt(T t)
专门处理 T 数据并输出 int 类型数据的函数。
ToLongFunction long applyAsLong(T t)
专门处理 T 数据并输出 long 类型数据的函数。
ToDoubleBiFunction double applyAsDouble(T t, U u)
专门处理 T 和 U 数据并输出 double 类型数据的函数。
ToIntBiFunction int applyAsInt(T t, U u)
专门处理 T 和 U 数据并输出 int 类型数据的函数。
ToLongBiFunction long applyAsLong(T t, U u)
专门处理 T 和 U 数据并输出 long 类型数据的函数。
运算符 Operator
运算符的特点:所有操作数的类型和结果的类型都是一样的,是属于特殊的函数。因此 Operator 可以继承 Function,当然不继承也可以( ﹁ ﹁ ) ~→。
一元运算符
UnaryOperator 中的 Unary
就是“一元”的意思,该接口代表一个对输入参数进行处理,并返回同类型数据的函数。
T apply(T t)
继承自 Function,输入 T 对象,输出 T 类型数据。Lambda 签名为 t -> {...return t/newT}
,返回与参数类型相同的结果。
publicinterfaceRunnable{publicabstractvoidrun();}5
使用:
publicinterfaceRunnable{publicabstractvoidrun();}6
二元运算符
BinaryOperator 用于表示一个对两个相同类型的输入参数进行处理,并返回相同类型结果的函数。
T apply(T t1, T t2)
继承自 BiFunction,输入两个 T 对象,输出 T 类型数据,Lambda 签名为 (t1, t2) -> {...return t3}
,用于将 t1 和 t2 处理为 t3。
publicinterfaceRunnable{publicabstractvoidrun();}7
使用:
publicinterfaceRunnable{publicabstractvoidrun();}8
特化运算符
DoubleUnaryOperator double applyAsDouble(double operand)
专门用于操作 double 类型的一元运算符。
IntUnaryOperator int applyAsInt(int operand)
专门用于操作 int 类型的一元运算符。
LongUnaryOperator long applyAsLong(long operand)
专门用于操作 long 类型的一元运算符。
DoubleBinaryUnaryOperator double applyAsDouble(double left, double right)
专门用于操作 double 类型的一元运算符。
IntBinaryUnaryOperator int applyAsInt(int left, int right)
专门用于操作 int 类型的一元运算符。
LongBinaryUnaryOperator long applyAsLong(long left, long right)
专门用于操作 long 类型的一元运算符。
供应器 Supplier
供应器的特点是不需要任何输入数据,供应器会向外提供数据,也就是对外输出数据。
Supplier 可以对外供应任意类型的数据。该接口表示一个提供某种类型数据的函数。
T get()
无输入,输出 T 类型数据,Lambda 签名为 () -> {...return t}
,根据函数内的逻辑生成 T 类型数据。
publicinterfaceRunnable{publicabstractvoidrun();}9
使用:
//Thread::Thread(Runnabletarget)newThread(()->System.out.println("HELLOWORLD")).start();0
特化供应器
BooleanSupplier boolean getAsBoolean();
专门提供 boolean 数据的供应器。
DoubleSupplier double getAsDouble();
专门提供 double 数据的供应器。
IntSupplier int getAsInt();
专门提供 int 数据的供应器。
LongSupplier long getAsLong();
专门提供 long 数据的供应器。
函数式接口汇总
无参无返回值
void
--单参无返回值
void
<T>
-DoubleConsumer::acceptvoid
double
-IntConsumer::acceptvoid
int
-LongConsumer::acceptvoid
long
-双参无返回值
void
<T>
<U>
ObjDoubleConsumer::acceptvoid
<T>
double
ObjIntConsumer::acceptvoid
<T>
int
ObjLongConsumer::acceptvoid
<T>
long
无参有返回值
<T>
--BooleanSupplier::getAsBooleanboolean
--DoubleSupplier::getAsDoubledouble
--IntSupplier::getAsIntint
--LongSupplier::getAsLonglong
--单参有返回值
boolean
<T>
-DoublePredicate::testboolean
double
-IntPredicate::testboolean
int
-LongPredicate::testboolean
long
-Function::apply<R>
<T>
-DoubleFunction::apply<R>
double
-DoubleToIntFunction::applyAsIntint
double
-DoubleToLongFunction::applyAsLonglong
double
-IntFunction::apply<R>
int
-IntToDoubleFunction::applyAsDoubledouble
int
-IntToLongFunction::applyAsLonglong
int
-LongFunction::apply<R>
long
-LongToDoubleFunction::applyAsDoubledouble
long
-LongToIntFunction::applyAsIntint
long
-ToDoubleFunction::applyAsDoubledouble
<T>
-ToIntFunction::applyAsIntint
<T>
-ToLongFunction::applyAsLonglong
<T>
-UnaryOperator::apply<T>
<T>
-DoubleUnaryOperator::applyAsDoubledouble
double
-IntUnaryOperator::applyAsIntint
int
-LongUnaryOperator::applyAsLonglong
long
-双参有返回值
<R>
<T>
<U>
ToDoubleBiFunction::applyAsDoubledouble
<T>
<U>
ToIntBiFunction::applyAsIntint
<T>
<U>
ToLongBiFunction::applyAsLonglong
<T>
<U>
BinaryUnaryOperator::apply<T>
<T>
<T>
BinaryUnaryOperator::applyAsDoubledouble
double
double
BinaryUnaryOperator::applyAsIntint
int
int
BinaryUnaryOperator::applyAsLonglong
long
long
作者:辐射工兵