函数式接口

简介: 本文介绍了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

目录
打赏
0
0
0
0
179
分享
相关文章
Java 最新技术实操:从基础到进阶的详细指南
本文介绍了Java 17及后续版本的核心技术实操,涵盖新特性、集合框架、异常处理和多线程编程等内容。主要包括:密封类(Sealed Classes)的继承层级控制、模式匹配(Pattern Matching)简化类型判断、文本块(Text Blocks)处理多行字符串;集合框架中的工厂方法和Stream API高级操作;异常处理的最佳实践如自动资源管理(ARM)和自定义异常;多线程编程中的CompletableFuture异步编程和ReentrantLock显式锁使用。
119 6
一文带你从入门到实战全面掌握RocketMQ核心概念、架构部署、实践应用和高级特性
本文详细介绍了分布式消息中间件RocketMQ的核心概念、部署方式及使用方法。RocketMQ由阿里研发并开源,具有高性能、高可靠性和分布式特性,广泛应用于金融、互联网等领域。文章从环境搭建到消息类型的实战(普通消息、延迟消息、顺序消息和事务消息)进行了全面解析,并对比了三种消费者类型(PushConsumer、SimpleConsumer和PullConsumer)的特点与适用场景。最后总结了使用RocketMQ时的关键注意事项,如Topic和Tag的设计、监控告警的重要性以及性能与可靠性的平衡。通过学习本文,读者可掌握RocketMQ的使用精髓并灵活应用于实际项目中。
1242 7
 一文带你从入门到实战全面掌握RocketMQ核心概念、架构部署、实践应用和高级特性
Node.js中发起HTTP请求的五种方式
以上五种方式,尽管只是冰山一角,但已经足以让编写Node.js HTTP请求的你,在连接世界的舞台上演奏出华丽的乐章。从原生的 `http`到现代的 `fetch`,每种方式都有独特的风格和表现力,让你的代码随着项目的节奏自由地舞动。
299 65
|
4月前
|
Ubuntu中dpkg和apt命令:debian包安装详解
希望这让你对于Ubuntu中的dpkg和apt命令有了更为清晰的理解。下次你面对软件包安装的问题,就可以轻松应对,优雅地在你的Linux系统中游刃有余了。
359 10
SpringCloudGateway网关服务实现文件上传功能
SpringCloudGateway网关服务实现文件上传功能
259 9
GLM-4V-Flash:智谱 AI 免费开放的图像理解大模型 API 接口
智谱AI推出的GLM-4V-Flash是一款专注于图像理解的免费开放大模型,提供API接口支持用户上传图片URL或Base64编码图片获取详细的图像描述。该模型通过深度学习和卷积神经网络技术,简化了图像分析流程,提高了开发效率,适用于内容审核、辅助视障人士、社交媒体、教育和电子商务等多个应用场景。
2111 14
GLM-4V-Flash:智谱 AI 免费开放的图像理解大模型 API 接口
IntelliJ IDEA - application.yml 文件不显示 Spring 小绿叶图标而显示小网格图标解决方案
IntelliJ IDEA - application.yml 文件不显示 Spring 小绿叶图标而显示小网格图标解决方案
2956 0
IntelliJ IDEA - application.yml 文件不显示 Spring 小绿叶图标而显示小网格图标解决方案
开发工具系列 之 同一个电脑上安装多个版本的JDK
这篇文章介绍了如何在一台电脑上安装和配置多个版本的JDK,包括从官网下载所需JDK、安装过程、配置环境变量以及如何查看和切换当前使用的JDK版本,并提到了如果IDEA和JDK版本不兼容时的解决方法。
开发工具系列 之 同一个电脑上安装多个版本的JDK
你的Redis真的变慢了吗?性能优化如何做
本文先讲述了Redis变慢的判别方法,后面讲述了如何提升性能。
103189 5
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问