我对Java 8 Functional Programming有疑问。我正在尝试使用函数式编程来实现某些功能,并且需要一些指导如何实现它。
我的要求是将每个方法执行包装在定时器函数中,这是方法执行的时间。以下是计时器功能和我需要计时的2个功能的例子。
timerMethod(String timerName, Function func){
timer.start(timerName)
func.apply()
timer.stop()
}
functionA(String arg1, String arg2)
functionB(int arg1, intArg2, String ...arg3)
我正试图通过functionA
& functionB
至timerMethod
,functionA
& functionB
期望不同的数字&执行的参数类型。
任何想法我如何实现它。
谢谢!
答案 0 :(得分:4)
您应该通过Separation of Concerns将它分成两部分,以使您的代码易于使用和维护。一个是时间,另一个是调用,例如:
// v--- invoking occurs in request-time
R1 result1 = timerMethod("functionA", () -> functionA("foo", "bar"));
R2 result2 = timerMethod("functionB", () -> functionB(1, 2, "foo", "bar"));
// the timerMethod only calculate the timing-cost
<T> T timerMethod(String timerName, Supplier<T> func) {
timer.start(timerName);
try {
return func.get();
} finally {
timer.stop();
}
}
IF 您希望返回功能界面而不是该方法的结果,您可以按以下方式完成:
Supplier<R1> timingFunctionA =timerMethod("A", ()-> functionA("foo", "bar"));
Supplier<R2> timingFunctionB =timerMethod("B", ()-> functionB(1, 2, "foo", "bar"));
<T> Supplier<T> timerMethod(String timerName, Supplier<T> func) {
// v--- calculate the timing-cost when the wrapper function is invoked
return () -> {
timer.start(timerName);
try {
return func.get();
} finally {
timer.stop();
}
};
}
IF 所有功能的返回类型为void
,您可以将Supplier
替换为Runnable
,然后制作timerMethod
& #39; s返回类型为void
&amp;从return
中删除timerMethod
个关键字。
IF 您的某些功能会引发检查异常,您可以将Supplier
替换为Callable
&amp;改为调用Callable#call
。
答案 1 :(得分:1)
不要抓住参数,然后在最后一刻传递它们。立即传递它们,但是通过用另一个函数包装来延迟调用函数:
Producer<?> f1 =
() -> functionA(arg1, arg2);
Producer<?> f2 =
() -> functionB(arg1, arg2, arg3);
这里,我将每个函数调用包装在一个带有0个参数的lambda(() ->...
)中。然后,稍后在没有参数的情况下调用它们:
f1()
f2()
这形成了对你在lambda中提供的参数的闭包,它允许你稍后使用这些变量,即使它们通常会被GC用于超出范围。
注意,我有一个?
作为Producer
的类型,因为我不知道你的函数返回什么类型。将?
更改为每个函数的返回类型。
答案 2 :(得分:1)
其他答案显示了如何使用闭包来捕获函数的参数,无论它的编号如何。这是一个很好的方法,非常有用,如果您事先知道参数,那么就可以捕获它们。
在这里,我想展示另外两种不需要事先知道参数的方法......
如果你以抽象的方式思考它,那么就没有带有多个参数的函数。函数接收一组值(也称为元组),或者它们接收一个参数并返回另一个接收另一个参数的函数,该函数又返回另一个返回的单参数函数...等,序列的最后一个函数返回实际结果(又名 currying )。
但是,Java中的方法可能有多个参数。所以挑战是构建总是接收一个参数的函数(通过元组或currying),但实际上调用接收多个参数的方法。
所以第一种方法是使用Tuple
辅助类,让你的函数接收一个元组,Tuple2
或Tuple3
:
因此,您的示例的functionA
可能会收到一个 Tuple2<String, String>
作为参数:
Function<Tuple2<String, String>, SomeReturnType> functionA = tuple ->
functionA(tuple.getFirst(), tuple.getSecond());
您可以按如下方式调用它:
SomeReturnType resultA = functionA.apply(Tuple2.of("a", "b"));
现在,为了使用functionA
方法装饰 timerMethod
,您需要做一些修改:
static <T, R> Function<T, R> timerMethod(
String timerName,
Function<? super T, ? extends R> func){
return t -> {
timer.start(timerName);
R result = func.apply(t);
timer.stop();
return result;
};
}
请注意,您应使用try/finally
块来增强代码的稳健性,如holi-java's answer所示。
以下是您使用timerMethod
方法functionA
的方法:
Function<Tuple2<String, String>, SomeReturnType> timedFunctionA = timerMethod(
"timerA",
tuple -> functionA(tuple.getFirst(), tuple.getSecond());
你可以调用timedFunctionA
作为任何其他函数,在调用时传递参数 now :
SomeReturnType resultA = timedFunctionA.apply(Tuple2.of("a", "b"));
您可以对示例的functionB
采取类似的方法,除了您需要使用Tuple3<Integer, Integer, String[]>
作为参数(处理varargs参数)。
这种方法的缺点是你需要创建许多Tuple
类,即Tuple2
,Tuple3
,Tuple4
等,因为Java缺乏内置支持为了元组。
另一种方法是使用一种名为currying的技术,即接受一个单一参数的函数,并返回另一个接受另一个参数的函数,等等,该序列的最后一个函数返回实际结果。 / p>
以下是如何为2参数方法创建一个currified函数functionA
:
Function<String, Function<String, SomeReturnType>> currifiedFunctionA =
arg1 -> arg2 -> functionA(arg1, arg2);
按如下方式调用它:
SomeReturnType result = currifiedFunctionA.apply("a").apply("b");
如果您想使用上面定义的currifiedFunctionA
方法装饰 timerMethod
,您可以执行以下操作:
Function<String, Function<String, SomeReturnType>> timedCurrifiedFunctionA =
arg1 -> timerMethod("timerCurryA", arg2 -> functionA(arg1, arg2));
然后,完全按照任何已加工的函数调用timedCurrifiedFunctionA
:
SomeReturnType result = timedCurrifiedFunctionA.apply("a").apply("b");
请注意,您只需要修饰序列的最后一个函数,即实际调用该方法的函数,这就是我们想要测量的函数。
对于您的示例的方法functionB
,您可以采用类似的方法,但现在是已经过化解的函数的类型:
Function<Integer, Function<Integer, Function<String[], SomeResultType>>>
至少可以说,这非常麻烦。因此,这是Java中经过限制的函数的缺点:表达其类型的语法。另一方面,currified函数非常方便使用,并允许您应用多种函数式编程技术,而无需编写辅助类。