🧾 概述
在日常开发中,我们经常需要对方法进行耗时统计。传统方式通常是手动在每个方法前后添加计时逻辑,这不仅冗余,还不易维护。本文介绍如何使用 Java 8 函数式接口(如 Function
、BiFunction
、Consumer
等)进行封装,使耗时统计更简洁、通用、可复用。
介绍
Function
接口是 Java 8 引入的函数式接口,简单来说,它可以将一个方法封装成 Function
对象,作为参数传递。Java 8 的 java.util.function
包包含了许多函数式接口,今天我们将简单介绍我常用的四种接口:
-
Function:接受一个参数并返回一个结果。
-
BiFunction:接受两个参数并返回一个结果。
-
Consumer:接受一个参数,但不返回任何结果。
-
BiConsumer:接受两个参数,但不返回任何结果。
这些接口帮助我们在方法中使用更简洁的方式传递行为,极大地提高了代码的灵活性和可读性。
🧠 传统写法(优化前)
可以看到,每个方法一次记录,那么就代表每个方法调用都需要写至少4行进行使用
public static void main(String[] args) {
final Long staTime = System.currentTimeMillis();
Demo2.test1("方法1");
final Long endTime = System.currentTimeMillis();
System.out.println(String.format("方法1耗时%s",endTime - staTime));
final Long staTime2 = System.currentTimeMillis();
Demo2.test1("方法2");
final Long endTime2 = System.currentTimeMillis();
System.out.println(String.format("方法2耗时%s",endTime2 - staTime2));
}
缺点:重复代码多,可读性和可维护性差。
🧩 使用函数式接口进行封装(优化后)
附上一个demo,当前demo中是我封装的上面4个类的方法。
✅ 封装类示例
import java.util.function.*;
/**
* @author 崔
* @version 0.0.1
* @Date 2023/8/15 19:51
*/
public class Demo {
/**
* 一个入参有返回值
*
* @param functionName 名称
* @param t 入参
* @param function 方法
* @param <R> 返回结果
* @return R
*/
public static <T, R> R oneValue(final String functionName,
final T t, final Function<T, R> function) {
final Long staTime = System.currentTimeMillis();
final R r = function.apply(t);
final Long endTime = System.currentTimeMillis();
System.out.println(String.format("方法%s耗时%s",functionName,endTime - staTime));
return r;
}
/**
* 两个入参一个返回值
*
* @param functionName 方法名称
* @param t 审核入参
* @param u 通用的service
* @param function 方法
* @param <R> 返回结果
* @return R
*/
public static <T, U, R> R twoValue(final String functionName,
final T t,
final U u,
final BiFunction<T, U, R> function) {
final Long staTime = System.currentTimeMillis();
final R r = function.apply(t, u);
final Long endTime = System.currentTimeMillis();
System.out.println(String.format("方法%s耗时%s",functionName,endTime - staTime));
return r;
}
/**
* 一个入参没有返回值
*
* @param functionName
* @param t
* @param function
*/
public static <T> void oneNot(final String functionName,
final T t,
final Consumer<T> function) {
final Long staTime = System.currentTimeMillis();
function.accept(t);
final Long endTime = System.currentTimeMillis();
System.out.println(String.format("方法%s耗时%s",functionName,endTime - staTime));
}
/**
* 两个入参没有返回值
*
* @param functionName
* @param t
* @param u
* @param function
* @param <T>
* @param <U>
*/
public static <T, U> void twoNot(final String functionName,
final T t,
final U u,
final BiConsumer<T, U> function) {
final Long staTime = System.currentTimeMillis();
function.accept(t, u);
final Long endTime = System.currentTimeMillis();
System.out.println(String.format("方法%s耗时%s",functionName,endTime - staTime));
}
}
✨ 使用示例
优化后,代码不但减少,而且更美观。
public static void main(String[] args) {
Demo.oneValue("方法1", "入参", Demo2::test1);//一个入参有返回值
Demo.twoValue("方法2", "入参1", "入参2", Demo2::test2);//两个入参有返回值
Demo.oneNot("方法1", "入参", Demo2::test3);//一个入参没返回值
Demo.twoNot("方法2", "入参1", "入参2", Demo2::test4);//两个入参没返回值
}
🔁 多参数泛用封装(进阶)
描述:无限入参并有返回值
优点:不限制入参数量
确定:入参需要用final修饰
下面看代码示例
✅ 自定义接口
import java.util.Objects;
import java.util.function.Function;
/**
* @author 崔
* @Date:2025/4/25 10:41
* @vesion: 0.0.1
*/
@FunctionalInterface
public interface ObjFunction<R> {
R apply(Object... args);
default <V> ObjFunction<V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (args) -> after.apply(apply(args));
}
}
✅ 泛用方法封装
public <R> R funApply(final String title,//标题
final ObjFunction<R> objFunction,//方法
final Object... o//方法入参
) {
final Long staTime = System.currentTimeMillis();
final R r = objFunction.apply(o);
final Long endTime = System.currentTimeMillis();
this.audLogTimeData(title, staTime, endTime);
return r;
}
✨ 使用示例
public static void main(String[] args) {
String result = Demo.funApply("方法2", (args) -> Demo2.test2("入参1", "入参2");//一个入参有返回值
}
✅ 总结
对于Function包中,还有很多其他的方法,比如返回boolean类型的参数,还可以自己封装多个参数的,目前封装好的最多两个入参,多个方法还能配合使用。