JDK8新特性--函数式接口--(Consumer的概念理解,模拟练习,企业实战)全流程彻底搞懂

背景,起因是因为在项目开发过程中,发现了一处代码的写法觉得很新奇看不懂,了解后发现是用到了函数式接口的知识。特此学习记录,整体过程梳理在本文。如果你不满足就会写个CURD,业务代码只会new来new去,代码维护性差,本文可以给你提供思路,告诉你写出一个巨屌的代码,同样的逻辑,不同的写法,其他程序员一眼看的哇塞和懵逼。

1 本人对【函数式接口】的理解

  • 为什么以前没听说过这个东西?

答:JDK8新特性,Java提出了函数式接口。

  • 为什么要使用函数式接口?

使用函数式接口,提高了代码的灵活性,后面的案例会带你体验有多吊,吊打你以前的写法

  • 什么是函数式接口?

接下来会从两个维度解释。

维度一:资料定义

希望第一步你能够看懂这个解释,能看懂 【匿名内部类】和【lambda表达式】写法

若果这一步吃力的话,可以参考这篇文章,写的很基础和全面,通俗易懂
点击查看-> 匿名内部类、Lambda、方法引用 的总结


维度二:JDK提供的函数式接口

对于新手的我们,使用函数式接口,基本上就是使用JDK提供的大量函数式接口。不同场景下使用不同的函数式接口,现在还没能力去自定义一个函数式接口,让项目代码有优雅。

在搜索资料的过程中,了解到了一个新名词:Java语法糖

(为了不影响本文梳理逻辑,关于语法糖的介绍我放到了章节,右侧目录可以查看)

这里作者想要强调的是,使用JDK提供的函数式接口,让我们代码更优雅了,某种程度上符合
Java语法糖的思想。理解这一点,也就能更透彻的理解函数式接口这个设计的优点。

好了,回到正题

  • JDK提供了哪些函数式接口呢?


    本文介绍的是java.util.function包下的接口,其中核心的有
  • Supplier接口
  • Consumer接口
  • Predicate接口
  • Function接口
  • 等等

下面会介绍 Consumer接口使用案例

2 函数式接口-->Consumer接口案例

现在有一个需求:创建一个list列表,存储数据,直接打印列表的内容

java 复制代码
@Test
public void demo(){
	List<String> list = Arrays.asList("John","Andy");
	printList(list);
}

public void printList(List<String> list){
	for(String s : list){
		System.out.println(s);
	}
}

现在我们的代码很优雅,客户需要打印功能,只需要调用printList方法就行,完全不需要操心如何实现的

现在客户需求变了:创建一个list列表,存储数据,将大写转化为小写后再打印列表的内容

java 复制代码
@Test
public void demo(){
	List<String> list = Arrays.asList("John","Andy");
	printList(list);
}

public void printList(List<String> list){
	for(String s : list){
		//变动的地方
		System.out.println(s.toLowerCase);
	}
}

按照客户需求,只需要修改一次printList方法的内容就行了。完美解决

现在需求又又变了:创建一个list列表,存储数据,将小写转化为大写后再打印列表的内容

java 复制代码
@Test
public void demo(){
	List<String> list = Arrays.asList("John","Andy");
	printList(list);
}

public void printList(List<String> list){
	for(String s : list){
		//变动的地方
		System.out.println(s.toUpperCase);
	}
}

再次按照客户需求,第二次修改printList方法的内容。完美解决

现在需求又又又又又变了...,是不是每次变动,都需要修改printList方法内容,这也太麻烦了。我打算把控制权给客户,需要什么自己去修改,不要动我 printList()方法的代码了。

如何实现呢,借用函数式接口Consumer

java 复制代码
@Test
public void demo(){
	List<String> list = Arrays.asList("John","Andy");
	printList(list, new Consumer<String>() {
		@Override
		public void accpet(String s){
			System.out.println(s);
			//System.out.println(s.toLowerCase);
			//System.out.println(s.toUpperCase);
		}
	}
}

public void printList(List<String> list,Consumer<String> consumer){
	for(String s : list){
		consumer.accept(s);
	}
}

这里使用了匿名内部类的写法,实际上不这样写,而是使用lambda表达式,优化后的代码

java 复制代码
@Test
public void demo(){
	List<String> list = Arrays.asList("John","Andy");
	printList(list, s-> System.out.println(s));
	}
}

public void printList(List<String> list,Consumer<String> consumer){
	for(String s : list){
		consumer.accept(s);
	}
}

分析一下,当程序运行后,开始调用printList方法,进入printList方法后,执行for循环,每次遍历都要执行accept方法,accept方法被重写了,就执行重写的内容。

当然了,看到这里你肯定会心有疑惑:我就是想要遍历+打印而已,怎么弄得这么复杂,还不如直接全写在demo()里面,使用最原始的方式。

这么想就错了,企业的项目是很复杂的,不是本案例演示的这些功能。

3 项目使用的案例

我现在得到一个json格式的字符串,需要将其解析后存到list中,得到list的长度,如果长度大于2,打印:我是2,否则打印:我是其他

3.1 普通写法

java 复制代码
文件A
public class JsonUtils {
    public int parseJsonString(String jsonString) {
        JSONArray jsonArray = JSON.parseArray(jsonString);
        List<Person> personList = jsonArray.toJavaList(Person.class);
        int length = personList.size();
        return length;
    }
}
java 复制代码
文件B
public class Example {
    public static void main(String[] args) {
        String jsonString = "[{\"name\":\"Alice\", \"age\":25}, {\"name\":\"Bob\", \"age\":30}]";
		
        int length = JsonUtils.parseJsonString(jsonString);
		
		printResult(length);
    }
    
	private static void printResult(int length) {
        if (length > 2) {
            System.out.println("我是2");
        } else {
            System.out.println("我是0");
        }
    }
}

这种普通写法是我们大家最习惯的写法,阅读起来完全没有问题。

注意分析主函数:我们一共传递了两次参数

那么接下来我们尝试修改为函数式接口的写法

3.2 lambda+函数式接口写法

看不懂代码,先看本章节后面结论

java 复制代码
文件A
public class JsonUtils {
    public static void parseJsonString(String jsonString, Consumer<Integer> consumer) {
        JSONArray jsonArray = JSON.parseArray(jsonString);
        List<Person> personList = jsonArray.toJavaList(Person.class);
        int length = personList.size();
        consumer.accept(length);
    }
}
java 复制代码
文件B

public class Example {
    public static void main(String[] args) {
        String jsonString = "[{\"name\":\"Alice\", \"age\":25}, {\"name\":\"Bob\", \"age\":30}]";

        Consumer<Integer> consumer = length -> {
            if (length > 2) {
                System.out.println("我是2");
            } else {
                System.out.println("我是其他");
            }
        };

		//将该Lambda表达式传递给JsonUtils类的parseJsonString方法,以执行解析JSON字符串、获取列表长度并打印结果信息的操作。
        JsonUtils.parseJsonString(jsonString, consumer);
    }
}

看看能否理解此代码?

可以看到此部分的代码使用了函数式接口+lambda表达式,希望大家先知道如何写这种风格的代码,作者知道大家一定一定会非常疑惑,改造后的代码怎么感觉更乱了,一点没看出来哪里优雅,总不能说使用了函数式接口就优雅。

注意看,粗看感觉很乱,仔细分析主函数内容:

可以发现,我们这次只传递了一次参数。只不过lambda表达式那一坨看着很恶心,不美观。要是能抽离出去,成为一个单独的方法就好了

3.3 方法引用+函数式接口写法

java 复制代码
A 文件

public class JsonUtils {
    public static void parseJsonString(String jsonString, Consumer<Integer> consumer) {
        JSONArray jsonArray = JSON.parseArray(jsonString);
        List<Person> personList = jsonArray.toJavaList(Person.class);
        int length = personList.size();
        consumer.accept(length);
    }
}
java 复制代码
B 文件

public class Example {
    public static void main(String[] args) {
        String jsonString = "[{\"name\":\"Alice\", \"age\":25}, {\"name\":\"Bob\", \"age\":30}]";

        Consumer<Integer> consumer = Example::printResult;

        JsonUtils.parseJsonString(jsonString, consumer);
    }

    private static void printResult(int length) {
        if (length > 2) {
            System.out.println("我是2");
        } else {
            System.out.println("我是其他");
        }
    }
}

这种写法简直太完美:传递一次参数,代码简洁。

4 总结

总结一下:

这里作者可以再具体说说有什么好处。

原客户需求:我现在得到一个json格式的字符串,需要将其解析后存到list中,得到list的长度,如果长度大于2,打印:我是2,否则打印:我是其他

现在客户需求变了,且需要你的同事负责开发:

  • 我现在得到一个json格式的字符串,需要将其解析后存到list中,得到list的长度,

  • 如果长度小于2,打印:我比2小;

  • 长度等于2,打印:我是2;

  • 长度大于2,打印:我比2大

如果你按照最初代码版本写的

你的同事:需求修改printResult()方法内容,然后在主函数进行测试:调用parseJsonString()方法获取返回值,将返回值传递给printResult()方法

如果你按照最初代码版本写的

你的同事:需求修改printResult()方法内容,然后在主函数进行测试:不需要改动代码

神奇吧,神奇吧,就是这么玩的。第一次写完后,以后的程序员不需要关心调用了什么方法,传递了什么参数,他需要做的仅仅就是修改业务代码printResult()。这代码质量太高了。拓展性,封装性,维护性都考虑到了。

5 我的企业代码实战案例

java 复制代码
文件A.java

@Override
    public void afterPropertiesSet() throws Exception {
        
          SyslogDataSyncConsumer<String, String> consumer = new SyslogDataSyncConsumer<String, String>;
          
          Integer syncCount = consumer.consumerInfo(
                           	Collections.singletonList(KafkaTopicConst.Event_BMS_SYSLOG_ROLE),
                            consumer::handle);
      }

     public void handle(ConsumerRecords<String, String> records) {........(业务功能处理)    }      
      
java 复制代码
文件B.java

public Integer consumerInfo(List<String> topics,
                                Consumer<ConsumerRecords<K, V>> recordsHandler)
  {
        // 拉取超时毫秒数,如果没有新的消息可供拉取,consumer会等待指定的毫秒数,到达超时时间后会直接返回一个空的结果集
        ConsumerRecords<K, V> records = null;
        
  		....
		//省略详细处理recods过程
		....
		
        //kafka消息处理器处理读取到的消息
        recordsHandler.accept(records);
  }

能看懂此代码吗?

内部的原理就是图示的流程

5 语法糖

通俗点解释,我们在写代码的时候,需要尽量争取让我们的代码更优雅,更容易理解。

比如正常遍历数组arrayTest

java 复制代码
for(int i = 0; i < arrayTest.seize(); i++){
	Sysytem.out.println(arrayTest[i]);
}

JDK1.5版本后,支持使用增强for循环写法

java 复制代码
for (String s : arrayTest) {
    Sysytem.out.println(s);     
}

可以看到这种效果是不是更优雅,更不容易出错(不用担心循环次数,数组下标是否正确)

还有哪些地方用到了语法糖,参考本文链接---->[点击查看]

10 JDK8的新特性有哪些

点击查看--> JDK8的新特性参考文章

相关推荐
天天扭码几秒前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶1 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺6 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
zwjapple12 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five13 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序13 分钟前
vue3 封装request请求
java·前端·typescript·vue
前端每日三省15 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
凡人的AI工具箱28 分钟前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
陈王卜31 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、31 分钟前
Spring Boot 注解
java·spring boot