详解Java 中的函数式接口

 更新时间:2021年12月23日 10:02:00   作者:独家雨天  
这篇文章主要为大家介绍了Java中的函数式接口,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助<BR>

@FunctionalInterface注解

如果你想自己定义个新的函数式接口,强烈建议你加上*@FunctionalInterface* 注解。可以更好地揭示我们定义这个接口的意思,同时也可以让编译器帮助我们检查接口定义的正确与否。在任何情况下,我们将一个接口只有一个抽象方法的接口都认为是函数是接口。这样的接口实现,才可以被看成是 lambda 的表达式。可能你会说,不对啊,明明我看到很多函数式接口是包含了多个方法的。这里需要说明一点的是,函数式接口只能有一个抽象方法,但是可以有多个默认实现的方法。

最简单的函数式接口

通常情况下,我们满足数据的映射,那就是输入一个数据,映射(对应)输出一个数据。

public interface Function<T, R> { … }	

比如,我们调用Map 方法中computeIfAbsent 方法来实现,当我们尝试获取 key 值为 John 的值是,如果不存在,则我们生成一个(key,key.length())的数据。

Map<String, Integer> nameMap = new HashMap<>();
Integer value = nameMap.computeIfAbsent("John", s -> s.length());

当然也可以采用冒号的语法糖

Integer value = nameMap.computeIfAbsent("John", String::length);

函数式接口含有一个compose 方法来组合 多个函数表达式。(类似于数学中嵌套表达式,y=f1(f2(x))

Function<Integer, String> intToString = Object::toString;
Function<String, String> quote = s -> "'" + s + "'";
Function<Integer, String> quoteIntToString = quote.compose(intToString);
assertEquals("'5'", quoteIntToString.apply(5));

其中第一个函数表达式是,将对象转换为字符串,第二个则是对字符串加上双引号。

基础数据类型的函数表达式

在 JDK 的 Function 包下的有很多基础类型的函数方法接口,但是这些接口都不是可以直接使用的,都需要自己实现。

  • IntFunction, LongFunction, DoubleFunction: 输入是具体的(Int,Long,Double),但是输出是可以指定的。
  • ToIntFunction, ToLongFunction, ToDoubleFunction: 输入是可以自定义的,输出是具体的(int,Long,Double)。
  • DoubleToIntFunction, DoubleToLongFunction, IntToDoubleFunction, IntToLongFunction, LongToIntFunction, LongToDoubleFunction: 输入和输出都是指定的基础数据类型。

下面让我们根据一个输入 Short 然后输出 Byte 数据类型,来说明用法。java.util.function 下不含这两个数据类型映射。

我们先定义一个函数式接口,从 Short 类型映射到 Byte 类型。

@FunctionalInterface
public interface ShortToByteFunction {
    byte applyAsByte(short s);
}

比如,我们实现如下的逻辑,输入一个 short 类型的数组,然后每个元素都应用我们定义函数式方法实现。

public byte[] transformArray(short[] array, ShortToByteFunction function) {
    byte[] transformedArray = new byte[array.length];
    for (int i = 0; i < array.length; i++) {
        transformedArray[i] = function.applyAsByte(array[i]);
    }
    return transformedArray;
}

然后 每个元素的逻辑,通过 lambda 来具体实现。比如,每个 将每个 short 类型都乘以 2 再转换成 Byte。

short[] array = {(short) 1, (short) 2, (short) 3};
byte[] transformedArray = transformArray(array, s -> (byte) (s * 2));
byte[] expectedArray = {(byte) 2, (byte) 4, (byte) 6};
assertArrayEquals(expectedArray, transformedArray);

二元输入参数的函数 Two-Arity Function Specializations

也就是输入两个不同的参数,输出一个指定数据类型的函数。在 JDK 中,带有 Bi 名称的就是类型。比如BiFunction, ToDoubleBiFunction, ToIntBiFunction, and ToLongBiFunction.

一个典型的用法,就是 Map 中的 replaceAll 方法。

Map<String, Integer> salaries = new HashMap<>();
salaries.put("John", 40000);
salaries.put("Freddy", 30000);
salaries.put("Samuel", 50000);
salaries.replaceAll((name, oldValue) -> 
  name.equals("Freddy") ? oldValue : oldValue + 10000);

其中,将 map 中每个元素 (key,value) 都引用 lambda 中函数表达式来重新应用。

Suppliers 供给型接口 & Consumers 消费型接口

可以理解为一个生产者,通常没有输入,但是能够更具特定规则输出数据(元素)。典型的应用就是一个序列生成器。JDK 里面有更丰富接口定义,如BooleanSupplier, DoubleSupplier, LongSupplier 和 IntSupplier.

int[] fibs = {0, 1};
Stream<Integer> fibonacci = Stream.generate(() -> {
    int result = fibs[1];
    int fib3 = fibs[0] + fibs[1];
    fibs[0] = fibs[1];
    fibs[1] = fib3;
    return result;
});

与Supplier 对应的 Consumer,接收一个输入参数,但是不返回任何数据类型。最常用的就是实现 foreach 中的消费每个迭代元素。

List<String> names = Arrays.asList("John", "Freddy", "Samuel");
names.forEach(name -> System.out.println("Hello, " + name));

Predicates 断言型接口

通常理解,该接口会返回 True 或 False 的数据类型,常见的就是 Stream 中的 Filter 接口实现的逻辑。

Operators

该函数式接口主要就是,输入一个数据类型,返回同样的数据类型。

names.replaceAll(String::toUpperCase);

有一个例外,就是 BinaryOperator 函数式接口是一个归并操作,汇聚和输入的列表元素。典型的入 reduce 函数。

List<Integer> values = Arrays.asList(3, 5, 8, 9, 12);
int sum = values.stream()
  .reduce(0, (i1, i2) -> i1 + i2);

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • springboot使用jasypt对配置文件加密加密数据库连接的操作代码

    springboot使用jasypt对配置文件加密加密数据库连接的操作代码

    这篇文章主要介绍了springboot使用jasypt对配置文件加密加密数据库连接的操作代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-01-01
  • Java中的JSONObject使用解读

    Java中的JSONObject使用解读

    本文详细介绍了Java中处理JSON数据的JSONObject类,包括创建、基本操作、高级特性和错误处理,通过示例代码和解释,读者可以掌握JSONObject的基本用法和处理技巧
    2025-01-01
  • idea插件篇之java内存分析工具(JProfiler)的使用

    idea插件篇之java内存分析工具(JProfiler)的使用

    这篇文章主要介绍了idea插件篇之java内存分析工具(JProfiler),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • Spring Cloud Eureka 服务上下线监控的实现

    Spring Cloud Eureka 服务上下线监控的实现

    这篇文章主要介绍了Spring Cloud Eureka 服务上下线监控的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • Maven中jar包下载失败的几种解决方法

    Maven中jar包下载失败的几种解决方法

    本文主要介绍了Maven中jar包下载失败的几种解决方法,包括配置国内Maven源、删除本地jar包目录重新下载,具有一定的参考价值,感兴趣的可以了解一下
    2025-02-02
  • 详解Java 缺失的特性扩展方法

    详解Java 缺失的特性扩展方法

    这篇文章主要为大家介绍了Java 缺失的特性扩展方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • 解决@PathVariable出现点号.时导致路径参数截断获取不全的问题

    解决@PathVariable出现点号.时导致路径参数截断获取不全的问题

    这篇文章主要介绍了解决@PathVariable出现点号.时导致路径参数截断获取不全的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Spring AOP定义AfterReturning增加实例分析

    Spring AOP定义AfterReturning增加实例分析

    这篇文章主要介绍了Spring AOP定义AfterReturning增加,结合实例形式分析了Spring面相切面AOP定义AfterReturning增加相关操作技巧与使用注意事项,需要的朋友可以参考下
    2020-01-01
  • Java开发实现猜拳游戏

    Java开发实现猜拳游戏

    这篇文章主要为大家详细介绍了Java实现猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • Jackson自定义序列化与反序列化注解详解

    Jackson自定义序列化与反序列化注解详解

    这篇文章主要介绍了Jackson自定义序列化与反序列化注解详解,某些场景下,我们使用Jackson对数据进行序列化或反序列化的时候,需要对某些数据进行特殊处理,需要的朋友可以参考下
    2023-11-11

最新评论