函数式接口的定义,是在接口上添加 @FunctionalInterface
注解,然后实现 Serializable
接口,以支持方法引用序列化、反射操作等特性。
import java.io.Serializable;
@FunctionalInterface
public interface MyFunc<T, F> extends Serializable {
F apply(T t);
}
这里我们定义了两个泛型类型,T
作为输入参数类型,F
作为输出结果类型。
例子1:简单使用
在使用上与 Java 标准库的 Function<T, R>
接口完全一致,支持 Lambda 表达式和方法引用。
MyFunc<String, String> func = i -> "hello, " + i;
String rs = func.apply("张三");
System.out.println(rs); // 输出:hello, 张三
当然,这种用法还可以封装成方法传参,配合泛型进一步提取参数类型、类结构等信息。
例子2:Lambda 解析
通过反射方式,配合 SerializedLambda
,我们可以提取 Lambda 表达式的底层实现细节,比如类名、方法名、字段名。这对于做框架封装、字段动态绑定等非常有用。
public <T> void test(MyFunc<T, ?> myFunc) {
try {
// 获取 Lambda 表达式内部的 writeReplace 方法
Method writeReplace = myFunc.getClass().getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
// 执行 writeReplace 方法,返回 SerializedLambda
SerializedLambda lambda = (SerializedLambda) writeReplace.invoke(myFunc);
// 提取实现类名、方法名
String className = lambda.getImplClass().replace('/', '.');
String methodName = lambda.getImplMethodName();
String fieldName = "";
// 推断字段名(处理 getter/is 开头的方法)
if (methodName.startsWith("get")) {
fieldName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
} else if (methodName.startsWith("is")) {
fieldName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
}
System.out.println("类名: " + className);
System.out.println("方法名: " + methodName);
System.out.println("属性名: " + fieldName);
} catch (Exception e) {
e.printStackTrace();
}
}
调用方式如下:
new Test().test(Users::getUserId);
输出效果如图:
例子3:动态字段提取到 SQL 语句
如果你正在构建一个 ORM 工具或者写通用查询构造器,那么可以通过上述接口拿到字段名,进而拼接 SQL:
public <T> String extractColumn(MyFunc<T, ?> func) {
try {
Method writeReplace = func.getClass().getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
SerializedLambda lambda = (SerializedLambda) writeReplace.invoke(func);
String methodName = lambda.getImplMethodName();
if (methodName.startsWith("get")) {
return Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
} else if (methodName.startsWith("is")) {
return Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 使用
String column = extractColumn(Users::getUserId);
System.out.println("SQL字段名: " + column);
## 例子4:结合泛型和实体字段自动映射
利用此特性,可以动态构建 Java Bean 到 Map 的转换方法、表单字段自动绑定等。
```java
public <T> Map<String, Object> buildParam(T entity, MyFunc<T, ?>... funcs) {
Map<String, Object> map = new HashMap<>();
for (MyFunc<T, ?> func : funcs) {
String key = extractColumn(func);
Object val = func.apply(entity);
map.put(key, val);
}
return map;
}
// 使用
Users user = new Users();
user.setUserId(100L);
user.setUserName("张三");
Map<String, Object> paramMap = buildParam(user, Users::getUserId, Users::getUserName);
System.out.println(paramMap);
总结:函数式接口的高级玩法
函数式接口 + 序列化 + 反射 = Java 动态编程的核心三板斧。
尤其适合以下场景:
- ORM字段绑定(如 MyBatis Plus、EasyQuery)
- 构建通用 Lambda 查询工具
- Java Bean 属性抽取(配合反射注解)
- 字段映射到 JSON、表单、SQL 字段名
- 框架级通用工具封装(比如权限注解、参数绑定)
如果你还停留在函数式接口只用来写 Lambda 表达式,那就太浪费它的威力了!