Java 8函数式编程 - 传递函数及其参数

时间:2017-07-14 14:48:06

标签: functional-programming java-8

我对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& functionBtimerMethodfunctionA& functionB期望不同的数字&执行的参数类型。

任何想法我如何实现它。

谢谢!

3 个答案:

答案 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),但实际上调用接收多个参数的方法。

方法#1:元组

所以第一种方法是使用Tuple辅助类,让你的函数接收一个元组,Tuple2Tuple3

因此,您的示例的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类,即Tuple2Tuple3Tuple4等,因为Java缺乏内置支持为了元组。

方法#2:滚动

另一种方法是使用一种名为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函数非常方便使用,并允许您应用多种函数式编程技术,而无需编写辅助类。