网站首页 > 基础教程 正文
函数式接口就是只定义一个抽象方法的接口。对于所有的函数式接口,我们都可以为它创建一个 Lambda 表达式。
本文是记录型文章,基本没有个人结论,如有错误,恳请指正,不胜感谢。 转载请于文首标明出处:【Java】JDK 提供的函数式接口 - 掘金 (juejin.cn)
函数式接口
在此简单介绍一下函数式接口。
什么是函数式接口
函数式接口就是只定义一个抽象方法的接口。由于 JDK8 开始提供 default 关键字,因此函数式接口并不仅仅只能定义一个方法,哪怕一个接口有很多默认方法和静态方法,只要只有一个抽象方法,它就是函数式接口。
函数式接口的作用是什么
函数式接口的作用在于,允许 Lambda 表达式以内联的形式作为函数式接口的实现,并把整个表达式作为它自身的实例。也就是说,Lambda 表达式可以作用在任何以函数式接口为方法参数的地方。
例如,Runnable 就是一个函数式接口,因为它里面只有 void run() 这一个抽象方法,因此可以将 Lambda 表达式作用在以 Runnable 为参数的地方。由于 Thread 的构造方法其中一个是 Thread(Runnable runnable),所以可以这样:
new Thread(() -> System.out.println("HELLO WORLD")).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 签名为 () -> {...},单纯地执行函数中的代码。
public interface Runnable {
public abstract void run();
}
复制代码
使用:
// Thread::Thread(Runnable target)
new Thread(() -> System.out.println("HELLO WORLD")).start();
复制代码
谓词 Predicate
一元谓词
Predicate 是“谓语”、“谓词”的意思。谓词表示用于判断,可以类比语法中的谓语,如 is、is not、是 和 不是。该接口用于表示一个对参数进行判断的函数。
boolean test(T t) 输入 T 对象,输出布尔值,Lambda 签名为 t -> {...return boolean},若 t 满足条件就返回 true,否则返回 false。
public interface Predicate<T> {
boolean test(T t);
// 创建 Predicate 函数链,相当于与另一个 Predicate 进行与运算
default Predicate<T> and(Predicate<? super T> other) {}
// 创建相反逻辑的 Predicate
default Predicate<T> negate() {}
// 创建 Predicate 函数链,相当于与另一个 Predicate 进行或运算
default Predicate<T> or(Predicate<? super T> other) {}
/**
* 创造一个 Predicate 函数,是一个全等函数,该函数用于判断输入参数是否等于 targetRef
*
* @param targetRef 全等函数的判断目标
* @param <T> 任意类型
* @return 一个 Equal 比较函数
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
复制代码
使用:
// ArrayList<E>::removeIf(Predicate<? super E> filter)
arrayList.removeIf(str -> str.length() > 42);
Predicate<Object> equalWithHelloWorld = Predicate.isEqual("HELLO WORLD");
arrayList.removeIf(equalWithHelloWorld);
复制代码
二元谓词
BiPredicate 也是谓词函数,不过可以接受两个操作数。名称中的 Bi 是“双”的意思,对于函数而言就是“二元”。该接口用于表示一个对两个参数进行合并判断的函数。
boolean test(T t, U u) 输入 T 对象和 U 对象,输出布尔值,Lambda 签名为 (t, u) -> {...return boolean},若 t 和 u 满足条件就返回 true,否则返回 false。
public interface BiPredicate<T, U> {
boolean test(T t, U u);
// 创建 BiPredicate 函数链,相当于另一个 BiPredicate 进行与运算
default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> other) {}
// 创建相反逻辑的 BiPredicate
default BiPredicate<T, U> negate() {}
// 创建 BiPredicate 函数链,相当于另一个 BiPredicate 进行或运算
default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> 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 进行处理,当然也可以不进行任何操作。
public interface Consumer<T> {
void accept(T t);
// 创建 Consumer 函数链,在当前 Consumer 处理结束后继续对 t 进行处理。
default Consumer<T> andThen(Consumer<? super T> after) {}
}
复制代码
使用:
// ArrayList<E>::forEach(Consumer<? super E> 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。
public interface BiConsumer<T, U> {
void accept(T t, U u);
// 创建 BiConsumer 函数链,在当前 BiConsumer 处理结束后继续对 t 和 u 进行处理。
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {}
}
复制代码
使用:
// HashMap<K, V>::forEach(BiConsumer<? super K, ? super V> action)
hashMap.forEach((key, value) -> System.out.println(key + " => " + value));
复制代码
特化消费者
- 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 类型数据。
public interface Function<T, R> {
R apply(T t);
// 创建 Function 函数链,在当前函数之前执行,<V, T> + <T, R> == <V, R>
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {}
// 创建 Function 函数链,在当前函数之后执行,<T, R> and <R, V> == <T, V>
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {}
/**
* 创建一个 Function 函数,该函数什么都不做,输入什么就直接返回什么
*
* @param <T> 任意类型
* @return 一个直接返回输入数据的函数
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
复制代码
使用:
// HashMap<K, V>::computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
hashMap.computeIfAbsent("ABC", strKey -> strKey.length());
hashMap.computeIfAbsent("ABC", String::length);
复制代码
二元函数
BiFunction 用于表示一个对两个输入参数进行处理,并返回处理结果的函数。
R apply(T t, U u) 输入 T 对象和 U 对象,输出 R 类型对象,Lambda 签名为 (t, u) -> {...return r},用于将 t 和 u 转化为 R 类型数据。
public interface BiFunction<T, U, R> {
R apply(T t, U u);
// 创建 BiFunction 函数链,在当前函数之后执行 Function,<T, U, R> + <R, V> == <T, U, V>
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {}
}
复制代码
使用:
// HashMap<K, V>::computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
hashMap.computeIfPresent("ABC", (strKey, intVal) -> intVal + strKey.length());
复制代码
特化函数
- 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},返回与参数类型相同的结果。
public interface UnaryOperator<T> extends Function<T, T> {
/**
* 创建一个 UnaryOperator,该函数直接返回输入的参数
*
* @param <T> 任意类型
* @return 一个输入和输出相同的函数
*/
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
复制代码
使用:
UnaryOperator<Integer> kbToMb = t -> t << 10;
// ArrayList::replaceAll(UnaryOperator<E> operator)
arrayList.replaceAll(kbToMb);
复制代码
二元运算符
BinaryOperator 用于表示一个对两个相同类型的输入参数进行处理,并返回相同类型结果的函数。
T apply(T t1, T t2) 继承自 BiFunction,输入两个 T 对象,输出 T 类型数据,Lambda 签名为 (t1, t2) -> {...return t3},用于将 t1 和 t2 处理为 t3。
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
/**
* 创建一个 BinaryOperator,该函数根据给定的排序规则,返回两个参数中小的那一个
*
* @param comparator 排序规则
* @param <T> 任意类型
* @return 一个输出最小值的函数
*/
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
/**
* 创建一个 BinaryOperator,该函数根据给定的排序规则,返回两个参数中大的那一个
*
* @param comparator 排序规则
* @param <T> 任意类型
* @return 一个输出最大值的函数
*/
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
复制代码
使用:
BinaryOperator<String> minString = BinaryOperator.minBy(String::compareTo);
// HashMap<K, V>::computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
hashMap.computeIfPresent("ABC", minString);
复制代码
特化运算符
- 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 类型数据。
public interface Supplier<T> {
T get();
}
复制代码
使用:
String string = randomBoolean.getAsBoolean() ? null : "HELLO WORLD";
Supplier<String> stringSupplier = () -> String.valueOf(System.currentTimeMillis());
// Objects::requireNonNull(T obj, Supplier<String> messageSupplier)
String s = Objects.requireNonNull(string, stringSupplier);
复制代码
特化供应器
- BooleanSupplier
boolean getAsBoolean();
专门提供 boolean 数据的供应器。 - DoubleSupplier
double getAsDouble();
专门提供 double 数据的供应器。 - IntSupplier
int getAsInt();
专门提供 int 数据的供应器。 - LongSupplier
long getAsLong();
专门提供 long 数据的供应器。
函数式接口汇总
- 无参无返回值 Functionreturnarg1arg2Runnable::runvoid--
- 单参无返回值 Functionreturnarg1arg2Consumer::acceptvoid<T>-DoubleConsumer::acceptvoiddouble-IntConsumer::acceptvoidint-LongConsumer::acceptvoidlong-
- 双参无返回值 Functionreturnarg1arg2BiConsumer::acceptvoid<T><U>ObjDoubleConsumer::acceptvoid<T>doubleObjIntConsumer::acceptvoid<T>intObjLongConsumer::acceptvoid<T>long
- 无参有返回值 Functionreturnarg1arg2Supplier::get<T>--BooleanSupplier::getAsBooleanboolean--DoubleSupplier::getAsDoubledouble--IntSupplier::getAsIntint--LongSupplier::getAsLonglong--
- 单参有返回值 Functionreturnarg1arg2Predicate::testboolean<T>-DoublePredicate::testbooleandouble-IntPredicate::testbooleanint-LongPredicate::testbooleanlong-Function::apply<R><T>-DoubleFunction::apply<R>double-DoubleToIntFunction::applyAsIntintdouble-DoubleToLongFunction::applyAsLonglongdouble-IntFunction::apply<R>int-IntToDoubleFunction::applyAsDoubledoubleint-IntToLongFunction::applyAsLonglongint-LongFunction::apply<R>long-LongToDoubleFunction::applyAsDoubledoublelong-LongToIntFunction::applyAsIntintlong-ToDoubleFunction::applyAsDoubledouble<T>-ToIntFunction::applyAsIntint<T>-ToLongFunction::applyAsLonglong<T>-UnaryOperator::apply<T><T>-DoubleUnaryOperator::applyAsDoubledoubledouble-IntUnaryOperator::applyAsIntintint-LongUnaryOperator::applyAsLonglonglong-
- 双参有返回值 Functionreturnarg1arg2BiFunction::apply<R><T><U>ToDoubleBiFunction::applyAsDoubledouble<T><U>ToIntBiFunction::applyAsIntint<T><U>ToLongBiFunction::applyAsLonglong<T><U>BinaryUnaryOperator::apply<T><T><T>BinaryUnaryOperator::applyAsDoubledoubledoubledoubleBinaryUnaryOperator::applyAsIntintintintBinaryUnaryOperator::applyAsLonglonglonglong
作者:辐射工兵
链接:https://juejin.cn/post/7005472838572310536
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 上一篇: 用好JAVA中的函数式接口,轻松从通用代码框架中剥离业务定制逻辑
- 下一篇: 什么是函数式接口?
猜你喜欢
- 2024-11-30 Java开发「函数式编程」——Lambda表达式
- 2024-11-30 玩转java8 lambda表达式三之对象方法引用
- 2024-11-30 Java 中的 Supplier:让数据生成更灵活
- 2024-11-30 Java 中的 Predicate:让判断逻辑更清晰灵活
- 2024-11-30 Java8精华-函数式编程(一)读完这篇,你将彻底理解
- 2024-11-30 你知道实现一个JAVA接口有几种方式吗?配合lambda更加意想不到
- 2024-11-30 java8精华-函数式编程-Predicate(四)
- 2024-11-30 巧妙应用 Java 的 Functional Interface:让代码更简洁、更灵活
- 2024-11-30 函数式接口编程真没那么难,简单几行让你的代码更优雅
- 2024-11-30 初窥函数式接口,不会取标题,没有噱头,全是干货
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)