使用Java8的函数式接口@FunctionalInterface实现简单异步调用

本文通过一个具体的例子展示了如何使用Java 8的函数式接口实现异步调用,包括定义函数式接口、参数包装类、监听类以及业务逻辑处理等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  最近研究了一下异步调用,接下来几篇博客是跟异步调用相关的,首先使用@FunctionalInterface接口实现一个简单的步调用,也就是本篇博客主要内容。

然后再加上ZMQ,实现一个带网络通信的异步调用。再下一步就是复杂一点的RPC调用,最终目的是实现一个使用ZMQ的分布式调用系统。

  Flag已经立,目标也定好了,先从简单的说起。

  情景假设:假设一个程序需求是用户Person查看用户密码,这个需要访问数据库,可能是耗时操作,把这个做成异步调用。

1  @FunctionalInterface介绍:

    FunctionalInterface的接口被称为函数式接口,该接口只能有一个自定义方法,但是可以包括从object类继承而来的方法。如果一个接口只有一个方  法,则编译器会认为这就是一个函数式接口

   定义一个@FunctionalInterface接口:

package com.zjq.java8.methodInterface;

@FunctionalInterface
public interface GofFunction<T1,T2> {
    public void execute(T1 t1,T2 t2);
}
2 定义一个参数包装类,可以包装任何参数:

 

package com.zjq.java8.methodInterface;

import java.util.HashMap;
import java.util.Map;

public class ParamContext {
    private Map<String,Object> datas=new HashMap<String,Object>();
	public ParamContext(Object...params){
		if(params==null||params.length==0){
			return;
		}
		for(int i=0;i<params.length;){
			datas.put((String) params[i], params[i+1]);
			i+=2;
		}
	}
	@SuppressWarnings("unchecked")
	public <R> R get(String key){
		return (R)datas.get(key);
	}
}
3 定义一个监听类,里面有两个map,一个是所调用方法的map,一个是对应参数的map,map的key值是生成的uuid

package com.zjq.java8.methodInterface;

import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class ListenCall {
	ConcurrentHashMap<String,GofFunction<ParamContext, ParamContext>> methodMap=new ConcurrentHashMap<String,GofFunction<ParamContext, ParamContext>>();
	ConcurrentHashMap<String,ParamContext> paramMap=new ConcurrentHashMap<String,ParamContext>();

	/**
	 * 获取请求编号
	 * @return
	 */
	public  String getCallId(){
		UUID uuid = UUID.randomUUID();
		return uuid.toString();
	}
	/**
	 * 监听返回值
	 * @param method
	 * @param callId
	 * @param context
	 */
	public void listenResult(GofFunction<ParamContext, ParamContext> method,String callId,ParamContext context) {
		methodMap.put(callId, method);
		paramMap.put(callId, context);
	}
	/**
	 * 等待处理结果
	 * @param result
	 * @param callId
	 */
	public  void waitForResult(ParamContext result,String callId){
		GofFunction<ParamContext, ParamContext> funtion=methodMap.get(callId);
		if(funtion!=null){
			ParamContext context=paramMap.get(callId);
			if(context==null){
				context=new ParamContext();
			}
				funtion.execute(result,context);
		}
		
	}

}
4 定义person的service类,里面有一些逻辑方法,有一个模拟读取数据库的方法,此方法是多线程的,模拟读取数据库,线程休眠4秒。

package com.zjq.java8.methodInterface;

public class PersonService {
	private ListenCall listenCall=new ListenCall();
	
	
	/**
	 * 这里模拟根据用户名从数据库查询密码
	 * @param name 用户名
	 * @param callId 请求调用的id
	 */
	public void getPwdFromDb(String name,String callId){
		new  Thread(new Runnable() {
			
			@Override
			public void run() {
				String sql="select from person where name="+"'"+name+"'";
				String pwd="1111";
				try {
					//这里等待4秒,模拟毒区数据库的书剑,时间有点夸张
					Thread.sleep(4000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				waitForResult(new ParamContext("pwd",pwd),callId);
			}
		}).start();
	}
	public String getCallId(){
		return listenCall.getCallId();
	}
	public void waitForResult(ParamContext p,String callId){
		listenCall.waitForResult(p, callId);
	}
	public void listenResult(GofFunction<ParamContext, ParamContext> method,String callId,ParamContext context){
		listenCall.listenResult(method, callId, context);
	}
}
5 用户的管理类。

 

package com.zjq.java8.methodInterface;

public class PersonManager {
	PersonService personService=new PersonService();


	public static void main(String[] args) {
		PersonManager callTest=new PersonManager();
		callTest.seePwd();

	}
	/**
	 * 假设用户想查看一下密码
	 */
	private void seePwd(){
		String name="1111";
		String time=String.valueOf(System.currentTimeMillis());
		String callId=personService.getCallId();
		personService.getPwdFromDb(name,callId);
		personService.listenResult(this::getPwd,callId,new ParamContext("time",time));
		System.out.println("数据库读取数据,可能有点耗时,这里做成异步操作,我先做其他事情。。。。。。。。。");
	}
	
	/**
	 * 获取到了密码
	 * @param result
	 * @param context
	 */
	private void getPwd(ParamContext result,ParamContext context){
		String pwd=result.get("pwd");
		long sed=(System.currentTimeMillis()-Long.valueOf(context.get("time")))/1000;
		System.out.println("经过"+sed+"秒"+"查询用户密码的调用终于返回了");
		System.out.println("得到密码:"+pwd);	
	}
}
6 程序执行结果:

数据库读取数据,可能有点耗时,这里做成异步操作,我先做其他事情。。。。。。。。。
经过4秒查询用户密码的调用终于返回了
得到密码:1111
分析,程序先打印的第一句,等4秒后才打印后面的,说明异步调用成功。



### 函数式接口的定义 在 Java 中,函数式接口是指**仅包含一个抽象方法的接口**。这种接口的主要目的是支持 Lambda 表达式的使用,从而简化代码并增强函数式编程的能力。虽然 Java 8 之前也有类似的概念(被称为“SAM 接口”),但直到 Java 8 引入 `@FunctionalInterface` 注解后,才正式标准化了函数式接口的定义。 函数式接口可以包含默认方法、静态方法以及私有方法,但必须确保只有一个抽象方法。例如: ```java @FunctionalInterface public interface Runnable { void run(); } ``` 该接口只定义了一个抽象方法 `run()`,因此是一个合法的函数式接口。即使不使用 `@FunctionalInterface` 注解,只要满足函数式接口的条件,它仍然是有效的函数式接口。然而,建议加上此注解以帮助编译器检查接口是否符合规范[^1]。 --- ### 函数式接口使用方法 #### 1. **自定义函数式接口** 开发者可以根据需求自定义函数式接口,并结合 Lambda 表达式使用。例如: ```java @FunctionalInterface public interface CalFunctionalInterface<T, R> { R cal(T t1, T t2); } ``` 这个接口定义了一个抽象方法 `cal()`,用于对两个泛型参数进行某种计算并返回结果。可以使用 Lambda 表达式实现接口的功能: ```java CalFunctionalInterface<Integer, Integer> add = (a, b) -> a + b; System.out.println(add.cal(3, 5)); // 输出 8 ``` #### 2. **Java 8 内置的函数式接口** Java 8 提供了一系列内置的函数式接口,适用于常见的函数式编程场景。例如: - **Predicate<T>**:接受一个输入并返回布尔值,常用于判断条件。 ```java Predicate<String> isEmpty = String::isEmpty; boolean result = isEmpty.test("Hello"); // 返回 false ``` - **Function<T, R>**:将一种类型的输入转换为另一种类型,适用于映射或转换操作。 ```java Function<String, Integer> lengthFunction = String::length; List<Integer> nameLengths = names.stream() .map(lengthFunction) .collect(Collectors.toList()); ``` - **Consumer<T>**:接受一个输入但不返回结果,通常用于执行某些副作用操作。 ```java Consumer<String> printMessage = System.out::println; printMessage.accept("Hello World"); ``` - **Supplier<T>**:不接受输入但返回一个结果,适用于懒加载或生成数据。 ```java Supplier<Double> randomGenerator = Math::random; double randomValue = randomGenerator.get(); // 返回随机数 ``` 这些内置接口极大地简化了开发流程,并提升了代码的可读性和可维护性[^2]。 --- ### 函数式接口的应用场景 函数式接口广泛应用于以下场景: - **Lambda 表达式**:作为 Lambda 表达式的赋值目标,使得代码更加简洁。 - **集合操作**:与 Stream API 结合使用实现高效的集合处理逻辑。 - **回调机制**:替代传统的匿名内部类,用于事件监听或异步任务处理。 - **函数组合**:通过默认方法如 `andThen()` 或 `compose()` 实现函数链式调用。 例如,使用 `Function` 接口进行函数组合: ```java Function<Integer, Integer> multiplyByTwo = x -> x * 2; Function<Integer, Integer> addFive = x -> x + 5; // 先乘以2,再加5 Function<Integer, Integer> combinedFunction = multiplyByTwo.andThen(addFive); int result = combinedFunction.apply(3); // 输出 11 ``` --- ### 总结 Java函数式接口函数式编程的核心概念之一,它允许开发者使用 Lambda 表达式来简化代码结构,并提高代码的可读性和灵活性。无论是自定义接口还是使用 Java 提供的内置接口函数式接口都在现代 Java 开发中扮演着重要角色。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值