【Java高级语法】(十四)函数式接口:与Lambda表达式共舞:探索Java函数式接口,赋予程序员更强大的编程能力!~

news2024/10/7 8:24:19

Java高级语法详解之函数式接口

  • 1️⃣ 概念
  • 2️⃣ 优势和缺点
  • 3️⃣ 使用
    • 3.1 函数式接口定义
    • 3.2 函数式接口实战
    • 3.3 使用技巧
  • 4️⃣ 内置函数式接口
  • 5️⃣ 应用场景
  • 🌾 总结

在这里插入图片描述

1️⃣ 概念

Java函数式接口起源于Java 8版本中的Lambda表达式和函数式编程特性的引入。在之前的Java版本中,Java一直是以面向对象的方式进行编程的。然而,随着函数式编程在其他编程语言中的流行,Java也意识到了函数式编程范式的重要性,并决定将其引入到语言中。

Java函数式接口的概念是指只包含一个抽象方法的接口。这样的接口可以被lambda表达式或方法引用赋值,从而使得代码更加简洁和灵活。 Java 8为了支持这个新的特性,在java.util.function包中添加了许多内置的函数式接口,如Function、Predicate和Consumer等。

函数式接口的引入使得Java能够以更函数式的方式进行编程,这种编程风格强调将方法视为一等公民,能够作为参数传递和返回结果。通过lambda表达式,我们可以用更少的代码来表达某些常见的模式,如迭代和过滤。这种编程范式为并行化处理和其他高级编程技术提供了更好的基础。

Java的函数式接口不仅受到了其他函数式编程语言的影响,还为Java程序员提供了更多的编程选择。它们鼓励开发者使用函数式编程范式解决问题,提高了代码的可读性和可维护性。

2️⃣ 优势和缺点

函数式接口具有以下优点:

  • 简洁性:函数式接口提供了一种简洁的方式来定义行为,减少了冗余的代码;
  • 可读性:使用Lambda表达式时,代码更易于理解,因为它们更注重描述要执行的操作,而非实现细节;
  • 易于并行化:函数式接口鼓励无状态和副作用的最小化,这使得并行化处理变得更加容易。

然而,函数式接口也存在一些限制:

  • 学习曲线:对于没有函数式编程经验的开发人员来说,理解和使用函数式接口可能需要一些时间和学习成本;
  • 降低可读性:有时,使用Lambda表达式可能会使代码变得更难以阅读,特别是对于复杂的逻辑或长篇幅的代码来说。

3️⃣ 使用

3.1 函数式接口定义

函数式接口(Functional Interface)是Java 8及更高版本中引入的概念,它是指只包含一个抽象方法的接口。函数式接口可以用作lambda表达式或方法引用的目标类型。

下面是定义一个函数式接口的格式:

@FunctionalInterface
interface MyFunctionalInterface {
    // 抽象方法
    void doSomething();
    
    // 默认方法(可选)
    default void doDefault() {
        // ...
    }
    
    // 静态方法(可选)
    static void doStatic() {
        // ...
    }
}

在上述代码中,使用了@FunctionalInterface注解来确保这是一个函数式接口。它是可选的,但推荐使用该注解,因为它可以帮助其他开发人员意识到该接口的特殊性。

函数式接口只能有一个抽象方法,但可以有默认方法和静态方法。默认方法提供了实现,允许在函数式接口中添加新功能而不破坏现有代码。静态方法通常用于提供一些工具方法,与特定的接口实例无关。

注意:如果一个接口被标记为函数式接口,并且存在两个抽象方法,编译器会抛出一个错误。函数式接口应且只能包含一个抽象方法,以确保具备函数式编程的能力。

3.2 函数式接口实战

下面是一个实际业务场景的Java案例程序,演示如何定义和使用一个Java函数式接口。

假设有一个订单系统,其中有一个订单类 Order,包含订单ID、顾客姓名和订单金额等属性。我们需要根据一组订单进行筛选,并返回金额大于给定阈值的订单列表。为了实现这个功能,我们可以使用函数式接口来定义一个过滤器,根据自定义条件对订单进行筛选。

首先,我们定义一个函数式接口 OrderFilter,其中只包含一个抽象方法 boolean filter(Order order),用于确定订单是否符合特定条件。接着,我们创建一个工具类 OrderUtils,在该类中定义一个静态方法 List<Order> filterOrders(List<Order> orders, OrderFilter filter),它将接收订单列表和订单过滤器作为参数,并返回符合条件的订单列表。

// 定义函数式接口
@FunctionalInterface
interface OrderFilter {
    boolean filter(Order order);
}

// 订单类
class Order {
    private int orderId;
    private String customerName;
    private double amount;

    // 省略构造函数和其他方法

    public int getOrderId() {
        return orderId;
    }

    public String getCustomerName() {
        return customerName;
    }

    public double getAmount() {
        return amount;
    }
}

// 工具类
class OrderUtils {
    public static List<Order> filterOrders(List<Order> orders, OrderFilter filter) {
        List<Order> filteredOrders = new ArrayList<>();

        for (Order order : orders) {
            if (filter.filter(order)) {
                filteredOrders.add(order);
            }
        }

        return filteredOrders;
    }
}

现在我们可以使用这个函数式接口和工具类来实现不同的订单筛选条件。如下面的程序:

import java.util.ArrayList;
import java.util.List;

public class UseFunctionalInterface {
    public static void main(String[] args) {
        // 获取订单列表
        List<Order> orders = new ArrayList<>();
        Order order1 = new Order(1, "张三", 182.5);
        Order order2 = new Order(2, "鲁智深", 99.0);
        Order order3 = new Order(3, "乌鸡国王", 101);
        orders.add(order1);
        orders.add(order2);
        orders.add(order3);

        List<Order> filteredOrders = OrderUtils.filterOrders(orders, new OrderFilter() {
            @Override
            public boolean filter(Order order) {
                return order.getAmount() > 100;
            }
        });
        System.out.println("金额大于$100的订单:" + filteredOrders);

        filteredOrders = OrderUtils.filterOrders(orders, order -> order.getAmount() < 100);
        System.out.println("金额小于$100的订单:" + filteredOrders);
    }
}

我想要筛选出金额大于100的订单,于是创建一个实现了 OrderFilter 接口的匿名内部类,
这样,filteredOrders 变量将包含金额大于100的订单列表。

除了使用匿名内部类,我们还可以使用 lambda 表达式来简化代码。
我想要筛选出金额小于100的订单,于是创建一个实现了 OrderFilter 接口的 lambda 表达式:

filteredOrders = OrderUtils.filterOrders(orders, order -> order.getAmount() < 100);

通过 lambda 表达式,我们可以更简洁地定义订单过滤条件。

运行结果:

金额大于$100的订单:[Order{orderId=1, customerName='张三', amount=182.5}, Order{orderId=3, customerName='乌鸡国王', amount=101.0}]
金额小于$100的订单:[Order{orderId=2, customerName='鲁智深', amount=99.0}]

总之,在实际业务场景中,使用函数式接口可以将方法作为参数传递,并以灵活的方式定义条件或操作。这样可以使我们的代码更加简洁、可读性更高,并提供更好的可扩展性和复用性。

3.3 使用技巧

使用函数式接口时,可以借助一些优化技巧来提高代码的性能和可读性:

  • 避免副作用:尽量避免在函数内部引入可变状态,以确保函数是无副作用的。
  • 利用并行处理:函数式接口鼓励使用流式操作,通过利用并行化处理来提高代码的执行效率。
  • 使用方法引用:当Lambda表达式只是简单地调用现有方法时,可以使用方法引用来提高代码的可读性。

4️⃣ 内置函数式接口

Java的内置函数式接口是在Java 8中引入的,它们为函数式编程提供了支持。以下是几个常用的内置函数式接口:

  1. Runnable:代表一个没有参数和返回值的代码块,通常用于多线程编程。

  2. Supplier<T>:表示一个生产者,不接受参数,返回一个结果。

  3. Consumer<T>:表示一个消费者,接受一个参数,无返回值。

  4. Function<T, R>:接受一个输入参数,并返回一个结果。

  5. Predicate<T>:接受一个参数,并返回一个布尔值结果,用于判断条件。

  6. UnaryOperator<T>:接受一个参数,并返回与参数类型相同的结果,相当于Function<T, T>

  7. BinaryOperator<T>:接受两个相同类型的参数,并返回一个与参数类型相同的结果。

除了以上列举的函数式接口,Java 8还引入了一些其他的接口,如BiConsumerBiFunctionBiPredicate等,分别表示带两个参数的消费者、函数和断言。这些函数式接口的设计使得Java能够进行函数式编程,并更加方便地利用Lambda表达式和方法引用来传递行为。

下面是一个案例程序,使用Supplier、Consumer、Function<T, R>和Predicate:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class CaseProgram {
    public static void main(String[] args) {
        // Supplier<T>: 生成一个随机整数作为结果
        Supplier<Integer> randomNumberSupplier = () -> (int) (Math.random() * 100);
        
        // Consumer<T>: 打印参数的平方
        Consumer<Integer> squarePrinter = number -> System.out.println("平方:" + number * number);
        
        // Function<T, R>: 将整数转换成字符串
        Function<Integer, String> integerToStringConverter = number -> "生成的随机数数字: " + number;
        
        // Predicate<T>: 判断奇数
        Predicate<Integer> oddNumberPredicate = number -> number % 2 != 0;
        
        // 调用示例
        int randomNum = randomNumberSupplier.get();
        squarePrinter.accept(randomNum);
        String numberString = integerToStringConverter.apply(randomNum);
        System.out.println(numberString);
        boolean isOdd = oddNumberPredicate.test(randomNum);
        System.out.println("是否为奇数: " + isOdd);

    }
}

运行结果:

平方:1225
生成的随机数数字: 35
是否为奇数: true

这个案例程序演示了如何使用Supplier<T>生成随机数,使用Consumer<T>对结果进行打印,使用Function<T, R>将整数转换为字符串,以及使用Predicate<T>判断奇数。

5️⃣ 应用场景

函数式接口主要在以下场景中发挥作用:

  • 函数作为参数:通过将函数作为参数传递给其他方法,可以实现回调机制、事件处理和分派

    • 回调机制:将一个函数式接口作为参数传递给另一个方法,当某个事件发生时,该方法会调用函数式接口的方法。这允许我们将特定的动作或逻辑委托给回调函数来处理,在适当的时候触发回调。因此,当事件发生时,可以通过调用回调函数来执行相应的操作;
    • 事件处理:通过将函数式接口作为监听器绑定到特定的事件上,当事件发生时,回调函数会被调用。例如,在JavaFX中可以使用setOnAction方法将一个事件处理程序绑定到按钮的点击事件上;
    • 分派:使用多态性和函数式接口可以实现动态分派的能力。通过将不同的函数式接口实现传递给同一个方法,根据传入的不同实现来执行不同的操作。这使得我们可以根据参数的类型或其他条件来决定具体要执行的逻辑。
  • 函数作为返回值:通过返回函数,可以创建灵活的API并实现高级功能,如延迟执行和惰性计算

    • 延迟执行:函数式接口与Lambda表达式一起使用可以实现延迟执行的效果。我们可以将逻辑封装在一个Lambda表达式中,并将其赋值给一个函数式接口。然后,只有在需要时才调用该函数式接口的方法,从而实现了延迟执行;
    • 惰性计算:通过使用Supplier函数式接口,可以实现惰性计算。Supplier接口表示一个供给型的函数,没有参数,返回一个结果。我们可以将需要延迟计算的逻辑封装在一个Supplier接口的实现中,在需要获取结果时,调用Supplier的get()方法触发计算。这样可以避免提前计算不必要的结果,节省资源。
  • 函数组合:通过将多个函数链接在一起,可以创建复杂的操作序列,从而实现更高级的功能。

    • 复杂操作序列:函数式编程的特点是操作序列的组合和转换。通过链式调用多个函数式接口的方法,可以创建复杂的操作序列。
      例如,Java 8的Stream API就是一个强大的函数式编程工具,它允许我们以一种流畅的方式对集合进行操作。我们可以使用Stream API对集合进行过滤、映射、排序等操作,最后通过终端操作来获取结果。在这个过程中,每个操作都会返回一个新的流,使得我们可以持续地进行更多的操作。这种函数式的操作序列使得代码更加简洁、易读和可维护。

🌾 总结

本文深入介绍了Java的函数式接口。函数式接口是一种只定义了一个抽象方法的接口,可以用于Lambda表达式和方法引用,使得Java代码更加简洁、易读和灵活。

首先,文章解释了为什么函数式接口在Java中如此重要。通过函数式接口,我们可以将行为作为参数传递给方法,从而实现更高层次的抽象和重用。这使得编写更具可读性和可维护性的代码成为可能。

然后,文章详细讨论了如何创建和使用函数式接口。我们可以使用@FunctionalInterface注解来标识一个接口是函数式接口,并确保它只有一个抽象方法。此外,文章还介绍了Java 8中新添加的一些内置函数式接口,如Consumer、Supplier、Predicate和Function等。通过了解这些内置接口的不同用途和特点,我们可以在编写代码时选择合适的接口,以适应不同的情况和需求。

同时,文章还提到了函数式接口的一些注意事项。由于函数式接口只能有一个抽象方法,因此我们应该遵循这个规则,并使用@FunctionalInterface注解来确保没有意外的错误。此外,虽然函数式接口可以包含默认方法,但我们应该谨慎使用默认方法,以避免引入复杂性和混淆。

总之,Java的函数式接口为我们提供了一种强大的工具,可以使代码更加简洁、灵活和可读。通过了解如何创建和使用函数式接口,以及如何配合Lambda表达式和方法引用,我们可以在编写Java代码时将其应用到实际场景中,并从中获得更大的便利和效益。

下一节我们继续探讨Lambda表达式和方法引用是如何与函数式接口配合使用的。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/679715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue基础(二)

Vue组件 组件化是Vue.js中的重要思想 它提供了一种抽象&#xff0c;让我们可以开发出一个个独立可复用的小组件来构造我们的应用。 任何的应用都会被抽象成一颗组件树。 注册组件 <body> <div id"app"> <!--3、使用组件--><my-cpn></m…

C语言小游戏——猜数字

猜数字游戏是一种简单而有趣的游戏&#xff0c;玩家需要根据提示猜出一个随机数。 //游戏菜单 void menu() {printf("*********************\n");printf("* 1.play *\n");printf("* 0.exit *\n");printf("********…

一道北大强基题背后的故事(五)——解数学题的数学模型是什么?

早点关注我&#xff0c;精彩不错过&#xff01; 上回说到&#xff0c;数学之美&#xff0c;美在作为一个工具的通用性&#xff0c;艺术追求和思维游戏&#xff0c;相关内容请戳&#xff1a; 一道北大强基题背后的故事&#xff08;四&#xff09;——数学之美&#xff0c;美在哪…

详解sentinel使用

目录 1.概述 2.下载安装 3.应用托管 4.流量控制 4.1.流控规则 4.2.流控模式 4.2.1.直接模式 4.2.2.关联模式 4.2.3.链路模式 4.3.流控效果 4.3.1.预热 4.3.2.排队等待 5.降级 5.1.降级规则 5.2.降级策略 5.2.1.RT 5.2.2.异常比例 5.2.3.异常数 1.概述 senti…

CentOS7设置nginx服务开机自启【开机自启】

方法一&#xff1a;使用Systemd 1.创建服务单元文件 sudo vi /etc/systemd/system/nginx.service2.编辑配置文件 [Unit] DescriptionThe NGINX HTTP and reverse proxy server Aftersyslog.target network.target[Service] Typeforking ExecStartPre/usr/sbin/nginx -t Exec…

[数据结构初阶]双链表

目录 双链表定义 初始化 创建节点 尾插 ​编辑 尾删 头插 头删 打印 查找 pos插入 头插复用 尾插复用 pos删除 头删复用 尾删复用 判空 size 销毁 完整代码 前面我们学习了单链表&#xff0c;但按照带头不带头(哨兵)和循环不循环我们可以有四种单链表&#…

知识蒸馏学习记录

最近在学习降噪处理不良天气的算法过程中&#xff0c;接触到了知识蒸馏&#xff0c;该算法作为一个深度学习通用算法&#xff0c;不仅广泛应用在自然语言处理方面&#xff0c;在计算机视觉等领域也广受追捧。 概要 简单来说&#xff0c;知识蒸馏就是将一个大的教师网络萃取到…

Nginx虚拟机主机

Nginx虚拟机主机 简述 虚拟主机是一种特殊软硬件技术&#xff0c;将网络上每一台计算机分成多个虚拟主机&#xff0c;每个虚拟主机可独立对外提供www服务&#xff0c;实现一台主机对外提供多个web服务&#xff0c;每个虚拟主机之间独立&#xff0c;互不影响。 配置位置 既可以在…

基于深度学习的高精度塑料瓶检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度塑料瓶检测识别系统可用于日常生活中或野外来检测与定位塑料瓶目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的塑料瓶目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检…

自动化面试题5

一、Modbus通信485/232/422的优缺点。 &#xff08;1&#xff09;RS232和RS422是全双工的&#xff0c;RS485是半双工的。 &#xff08;2&#xff09;RS485与RS232仅仅是通讯的物理协议&#xff08;即接口标准&#xff09;有区别&#xff0c;RS485是差分传输方式&#xff0c;R…

Spring6 AOT 提前编译

文章目录 1、AOT概述1.1、JIT与AOT的区别1.2、Graalvm1.3、Native Image 2、Native Image构建过程2.1、GraalVM安装&#xff08;1&#xff09;下载GraalVM&#xff08;2&#xff09;配置环境变量&#xff08;3&#xff09;安装native-image插件 2.2、安装C的编译环境&#xff0…

法规标准-ISO 23374标准解读

ISO 23374是做什么的&#xff1f; ISO 23374全名为智能交通系统-自动代客泊车系统(AVPS) 第一部分&#xff1a;系统框架、自动驾驶要求和通信接口&#xff0c;针对AVPS的系统框架及功能要求、通信接口进行介绍&#xff0c;由于通信接口涉及功能实现&#xff0c;但此处介绍较为…

ubuntu20.04 磁盘故障,然后重装22.04

ubuntu20.04 磁盘故障&#xff0c;然后重装22.04 重装原因开机自启动不需要使用sudo 软件截图 flameshot输入法 fcitx5 重装原因 编译程序报错 /usr/include/x86_64-linux-gnu/bits/signum.h:26:10: fatal error: /usr/include/x86_64-linux-gnu/bits/signum-generic.h: 结构需…

VUE L ∠脚手架 配置代理 ⑩⑧

目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ V u e j s Vuejs Vuejs初识 V u e C L I VueCLI VueCLI C L I CLI CLI V u e Vue Vue配置代理 C L I CLI CLI配置方法一 C L I CLI CLI配置方法二 C L I CLI CLI V u …

经典文献阅读之--VIP-SLAM(紧耦合RGB-D视觉惯性平面SLAM)

0. 简介 现有的视觉SLAM很多的算法让仍然是基于特征提取的方法来完成地图的建立&#xff0c;而RGB-D传感器的算法仍然是主要基于稀疏点的SLAM系统&#xff0c;这就导致在构建稠密点云地图的时候需要保持大量的地图点来建模环境。大量的地图点给我们带来了很高的计算复杂性&…

Python 进阶(二):Python使用ORM框架peewee操作MySQL数据库

Python使用ORM框架peewee操作数据库 前言1. 安装Peewee库并初始化数据库2. 创建数据库连接3. 定义数据表模型类4. 连接数据库并创建表5. 操作数据库5.1 插入数据5.2 查询数据5.3 更新数据5.4 删除数据 6. 聚合查询 前言 本文基于MySQL8.x版本的学习&#xff0c;python版本基于…

美客多、Newegg卖家如何提高店铺销量?测评自养号的重要性

作为美客多和Newegg平台上的卖家&#xff0c;提高店铺销量是卖家取得商业成功的关键。珑哥今天来说一些有效的策略&#xff0c;帮助卖家增加销售额并提升店铺的知名度和竞争力。 优化产品页面&#xff1a; 优秀的产品页面可以吸引更多买家并促进销售。确保产品标题准确、吸引人…

经典文献阅读之--NICE-SLAM(SLAM的神经隐含可扩展编码)

0. 简介 对于深度学习而言&#xff0c;NeRF一定是最近两年最火的工作之一了&#xff0c;**NeRF&#xff08;Neural Radiance Fields&#xff09;**是最早在2020年ECCV会议上的Best Paper&#xff0c;其将隐式表达推上了一个新的高度&#xff0c;仅用 2D 的 posed images 作为监…

stm32配置基本定时功能

Clock Source参数说明 内部时钟源是由STM32F103芯片内部的RC振荡器提供的。它的频率为8MHz&#xff0c;可以通过PLL倍频器进行倍频&#xff0c;最高可达到72MHz。 Internal clock Division No Division&#xff1a;不分频&#xff0c;即系统时钟直接作为时钟信号。Divided by 2…

ModaHub AI模型社区:向量数据库CPU 版 Milvus和GPU 版 Milvus 版本比较

目录 CPU 版 Milvus 版本比较 概述 CPU 版 Milvus 支持的索引类型 浮点型向量 二值型向量 GPU 版 Milvus 版本比较 概述 GPU 版 Milvus 支持的索引类型 浮点型向量 二值型向量 CPU 版 Milvus 版本比较 概述 Milvus 提供两个发行版本&#xff1a;CPU 版本和 GPU 版本…