函数式接口

简介: 本文介绍了Java中的函数式接口及其应用。函数式接口是指仅包含一个抽象方法的接口,可配合Lambda表达式简化代码。主要内容包括:1)函数式接口的概念与定义;2)函数式接口的使用场景,如作为方法参数、延迟执行(lambda优化日志案例)、作为返回值等;3)常用函数式接口详解,如Supplier(生产数据)、Consumer(消费数据)、Predicate(条件判断)、Function(类型转换)。每部分通过具体示例和代码解读,展示了函数式接口在实际开发中的灵活应用。

1、函数式接口的概念

函数式接口在java中是指:有且仅有一个抽象方法的接口,当然接口中也可以包含其他的方法(默认,静态,私有)。 函数式接口的定义:

csharp

体验AI代码助手

代码解读

复制代码

@FunctionalInterface
public interface MyFunctionalInterface {
    //定义一个抽象方法
    public abstract void method();

}
  • 接口中的抽象方法的public abstract可以省略。
  • @FunctionalInterface可以检测接口是否为函数式接口(是编译成功;否编译失败-接口中没有抽象方法或抽象方法个数多于一个)。

2.函数式接口的使用

2.1、作为方法的参数使用

typescript

体验AI代码助手

代码解读

复制代码

public class Demo01 {
    public static void show(MyFunctionalInterface myFunctionalInterface){
        myFunctionalInterface.method();
    }
    public static void main(String[] args) {
        //调用show方法,方法的参数是一个接口,所以可以传递接口的匿名内部类
        show(new MyFunctionalInterface() {
            @Override
            public void method() {
                System.out.println("匿名内部类重写接口中的抽象方法");
            }
        });
        //调用show方法,方法的参数是一个函数式接口,所以我们可以使用lambda表达式
        show(()->{
            System.out.println("使用lambda表达式重写接口中的抽象方法");
        });
        //简化lambda表达式
        show(()-> System.out.println("使用lambda表达式重写接口中的抽象方法"));
    }
}

2.2、lambda延迟执行

2.2.1、性能浪费的日志案例

typescript

体验AI代码助手

代码解读

复制代码

public class Demo02 {
    public static void showLog(int level, String log){
        if(level == 1){
            System.out.println(log);
        }
    }

    public static void main(String[] args){
        String msg1 = "hello";
        String msg2 = "world";
        String msg3 = "java";
        showLog(2, msg1 + msg2 + msg3);
    }
}

以上代码存在一些性能浪费的问题,调用showLog方法,是先把字符串拼接好,然后再调用showLog方法,如果传入的日志等级参数不是1,则不会输出拼接口的字符串,因此就白拼接了,存在浪费。

2.2.2、日志优化

csharp

体验AI代码助手

代码解读

复制代码

@FunctionalInterface
public interface MessageBuilder {
    //定义一个拼接消息的抽象方法,返回拼接后的字符串
    public abstract String builderMessage();
}

arduino

体验AI代码助手

代码解读

复制代码

public class Demo03Lambda {
    public static void showLog(int level, MessageBuilder mBuilder){
        if(level == 1) {
            System.out.println(mBuilder.builderMessage());
        }
    }
    public static void main(String[] args){
        String msg1 = "hello";
        String msg2 = "world";
        String msg3 = "java";
        /**
         * 使用lambda表达式作为参数传递,仅仅是把参数传递给showLog方法中,
         * 只有满足条件,日志的等级是1,才会调用接口MessageBuilder中的方法,
         * 才会进行字符串的拼接
         */

        showLog(1, ()->  msg1 + msg2 + msg3);
    }
}

2.3、函数式接口作为函数的参数案例

假设有一个startThread方法使用Runnable接口作为参数

typescript

体验AI代码助手

代码解读

复制代码

public class Demo04Lambda {
    //定义一个方法startThread,方法的参数使用函数式接口Runnable
    public static void startThread(Runnable runnable){
        new Thread(runnable).start();
    }

    public static void main(String[] args){
        //方法的参数是一个接口,可以传递这个接口的匿名内部类
        startThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
        //方法的参数是一个函数式接口,可以传递lambda表达式
        startThread(()-> System.out.println(Thread.currentThread().getName()));
    }
}

2.4、函数式接口作为函数的返回值案例

如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个lambda表达式。 假设需要一个方法来获取java.util.Comparator接口的对象作为排序器。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo05Lambda {
    public static Comparator<String> getComparator(){
//        return new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return o2.length() - o1.length();
//            }
//        };
        return (o1, o2) -> o2.length() - o1.length();
    }

    public static void main(String[] args){
        //创建一个字符串数组
        String[] arr = {"a", "bbb", "cc", "dddd"};
        //输出排序前的数组 [a, bbb, cc, dddd]
        System.out.println(Arrays.toString(arr));
        //调用Arrays中的sort方法,对字符串数组进行排序
        Arrays.sort(arr, getComparator());
        //输出排序后的数组 [dddd, bbb, cc, a]
        System.out.println(Arrays.toString(arr));
    }
}

3、常用的函数式接口

3.1、Supplier

java.util.function.Supplier接口仅包含一个无参的方法,:T get()。用来获取一个泛型参数执行类型的数据。该接口被称为生产型接口,指定接口的泛型是什么类型,get方法就会生产什么类型的数据。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo06Lambda {
    //定义一个方法,方法的参数传递Supplier<T>接口,泛型指定String,get方法就会返回一个String
    public static String getString(Supplier<String> supplier){
        return supplier.get();
    }

    public static void main(String[] args) {
        String s = getString(()-> "赵丽颖");
        System.out.println(s);
    }
}

3.1.2、求数组元素最大值案例

使用Supplier接口作为方法参数类型,通过lambda表达式获取数组中的最大值。

arduino

体验AI代码助手

代码解读

复制代码

public class Demo07Lambda {
    //定义一个方法,获取int类型数组中元素最大值,方法的参数传递Supplier接口,泛型使用Integer
    public static int getMax(Supplier<Integer> supplier) {
        return supplier.get();
    }
    public static void main(String[] args) {
        int[] arr = {1, 4, 211, 6, 8, 11, 111};
        int maxValue = getMax(()->{
           int max = arr[0];
           for(int i = 1; i< arr.length; i ++){
               if(arr[i]> max){
                   max = arr[i];
               }
           }
           return max;
        });
        System.out.println(maxValue);
    }
}

3.2、Consumer

java.util.function.Consumer接口与Supplier接口相反,它消费数据,数据类型由泛型执行,接口中只有一个抽象方法accept(T t),表示消费一个指定泛型的数据。 定义一个方法,参数传递Consumer接口,泛型使用String,Consumer接口进行消费字符串。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo08Lambda {
    public static void consumerStr(String name, Consumer<String> consumer) {
        consumer.accept(name);
    }

    public static void main(String[] args) {
        //调用consumerStr方法,传递字符串姓名,方法的另一个参数是一个函数式接口,可以传递lambda表达式
        consumerStr("古天乐", name -> {
            //消费方式:把字符串进行反转
            String revName = new StringBuffer(name).reverse().toString();
            System.out.println(revName);
        });
    }
}

3.2.1、默认方法andThen

可以把两个Consumer接口组合到一起,再对数据进行消费。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo09Lambda {
    public static void consumerStr(String s, Consumer<String> con1, Consumer<String> con2){
        //con1.accept(s);
        //con2.accept(s);
        //使用andThen方法,把两个Consumer接口连接到一起再消费
        con1.andThen(con2).accept(s);//con1连接con2,先执行con1消费数据,再执行con2消费数据
    }
    
    public static void main(String[] args) {
        consumerStr("Test", s -> System.out.println(s.toLowerCase()), s -> System.out.println(s.toUpperCase()));
    }
}

3.2.1.1、格式化打印案例

字符串数组中存在多条信息,按照"姓名":xx,"性别":xx,打印数据。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo10Lambda {
    public static void formatString(String s, Consumer<String> con1, Consumer<String> con2){
        con1.andThen(con2).accept(s);
    }
    public static void main(String[] args){
        String[] arrs = {"张三,男","李四,男", "王五,女"};
        for (String arr: arrs){
            formatString(arr, s->{
               String name = s.split(",")[0];
               System.out.print("姓名:"+name);
            }, s->{
                String sex = s.split(",")[1];
                System.out.print(",性别:"+sex);
            });
            System.out.println();
        }
    }
}

3.3、Predicate

对某种数据类型的数据进行判断,结果返回一个boolean值,该接口包含一个抽象方法:test(T t),用来对指定数据类型的数据进行判断,符合条件返回true;不符合条件,返回false。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo11Lambda {
    public static boolean checkString(String s, Predicate<String> predicate){
        return predicate.test(s);
    }

    public static void main(String[] args) {
        String s = "abcdfges";
        boolean isGreaterThanFive = checkString(s, str -> str.length()>5);
        System.out.println(isGreaterThanFive);
    }
}

3.3.1、默认方法and

Predicate接口中有一个方法and,表示并且关系,用于连接两个判断条件,两个判断条件均为true时,记结果为true。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo12Lambda {
    public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){
        return pre1.and(pre2).test(s);
    }

    public static void main(String[] args) {
        String s = "abcdewesdf";
        boolean isGreaterThanFiveAndContainsA = checkString(s, str->s.length() > 5, str-> s.contains("a"));
        System.out.println(isGreaterThanFiveAndContainsA);
    }
}

3.3.2、默认方法or

Predicate接口中有一个方法and,表示并且关系,用于连接两个判断条件,两个判断条件有一个为true时,记结果为true。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo12Lambda {
    public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){
        return pre1.or(pre2).test(s);
    }

    public static void main(String[] args) {
        String s = "aaa";
        boolean isGreaterThanFiveAndContainsA = checkString(s, str->s.length() > 5, str-> s.contains("a"));
        System.out.println(isGreaterThanFiveAndContainsA);
    }
}

4、Function

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一数据类型的数据,前者称为前置条件,后者称为后置条件,Function接口中最重要的抽象方法为R apply(T t),根据类型T的参数返回R类型的数据。

4.1、将String类型转换为Integer类型

typescript

体验AI代码助手

代码解读

复制代码

public class Demo13Lambda {
    public static int changeString2Int(String num, Function<String, Integer> function){
        return function.apply(num);
    }

    public static void main(String[] args) {
        int num = changeString2Int("14", str-> Integer.parseInt(str));
        System.out.println(num);
    }
}

4.2、默认方法andThen

可以把两个Function接口组合到一起,再对数据进行处理。

typescript

体验AI代码助手

代码解读

复制代码

public class Demo14Lambda {
    public static void change(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
        String s1 = fun1.andThen(fun2).apply(s);
        System.out.println(s1);
    }

    public static void main(String[] args) {
        change("123", s -> Integer.parseInt(s), num -> num + "");
    }
}


转载来源:https://juejin.cn/post/6994779763076235278

相关文章
|
25天前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
177 0
|
1月前
|
数据安全/隐私保护 Android开发 Windows
2025 年三款免费高清无水印视频录制工具推荐合集
本文介绍了三款免费高清录屏软件:EVCapture、Bandicam 和 屏幕录像机(oCam)。EVCapture 功能强大,支持视频录制与直播,提供分屏录制、实时按键显示等;Bandicam 适合游戏录屏,可自定义录制区域并添加Logo,还支持音频和摄像头设置;oCam 小巧灵活,支持多种视频格式(如GIF、MP4等)及音频、截图功能。三者均无水印,画质清晰,满足不同录屏需求。资源地址已提供,可供下载体验。
1182 0
|
2月前
|
运维 安全 网络安全
443端口:HTTPS通信的安全基石
作为互联网安全的基石,443端口通过加密与认证机制,保护了数十亿用户的隐私与数据完整性。无论是开发者、运维人员还是普通用户,理解其原理与作用都至关重要。在享受便利的同时,也需时刻关注安全实践,防范潜在风险。
310 12
|
26天前
|
前端开发 JavaScript
CSS变量实战:动态主题切换技巧
CSS变量实战:动态主题切换技巧
158 81
|
3月前
|
Ubuntu Linux
Ubuntu中dpkg和apt命令:debian包安装详解
希望这让你对于Ubuntu中的dpkg和apt命令有了更为清晰的理解。下次你面对软件包安装的问题,就可以轻松应对,优雅地在你的Linux系统中游刃有余了。
301 10
|
4月前
|
存储 弹性计算 安全
阿里云服务器付费类型、地域、镜像、存储、带宽和安全组设置与选择注意事项参考
在我们通过自定义购买的方式购买阿里云服务器器ECS时,会有多个选项,有的新手用户可能并不是很清楚这些选项是什么,选择或设置时需要注意什么,本文将从付费类型、地域与可用区、镜像、存储、带宽和安全组等多个方面,为您详细解析云服务器购买过程中各个参数与配置的选择注意事项,以供参考。
334 66
|
26天前
|
存储 固态存储 Linux
VMware ESXi 9.0 正式版发布下载 - 领先的裸机 Hypervisor
VMware ESXi 9.0 正式版发布下载 - 领先的裸机 Hypervisor
341 1
VMware ESXi 9.0 正式版发布下载 - 领先的裸机 Hypervisor
|
5月前
|
JavaScript 前端开发 数据可视化
【01】Cocos游戏开发引擎从0开发一款游戏-cocos环境搭建以及配置-Cocos Creator软件系统下载安装-node环境-优雅草卓伊凡
【01】Cocos游戏开发引擎从0开发一款游戏-cocos环境搭建以及配置-Cocos Creator软件系统下载安装-node环境-优雅草卓伊凡
230 2
【01】Cocos游戏开发引擎从0开发一款游戏-cocos环境搭建以及配置-Cocos Creator软件系统下载安装-node环境-优雅草卓伊凡
|
5月前
|
SQL 关系型数据库 MySQL
云服务器常用端口作用
了解云服务器常用端口的作用有助于高效管理资源、快速定位问题及更好地使用云服务。常见端口包括:21(FTP,文件传输)、22(SSH,远程连接Linux)、25(SMTP,发送邮件)、80(HTTP,网页服务)、110/143(POP3/IMAP,接收邮件)、443(HTTPS,加密网页)、1433(SQL Server)、3306(MySQL)、3389(RDP,远程访问Windows桌面)和8080(代理服务)。
170 2
|
26天前
|
监控 前端开发 JavaScript
不用WebSocket也能搞定实时消息推送?试一试SSE吧!
在现代 Web 开发中,实时数据更新至关重要,如股票行情、聊天消息等。SSE(Server-Sent Events)是一种基于 HTTP 的简单技术,可实现服务器向客户端推送实时通知。相比 WebSocket,SSE 单向通信、易于实现且具备自动重连机制。本文通过 Go 语言与 Gin 框架,演示了如何构建 SSE 实时时间推送功能。服务端代码设置必要响应头并使用定时器发送数据,客户端通过 `EventSource` 接收并展示消息。此外,还探讨了性能优化及扩展场景,如监控仪表盘和任务进度更新,帮助开发者在实际项目中应用这一高效技术。
93 2
不用WebSocket也能搞定实时消息推送?试一试SSE吧!