日常调戏Java:方法的分步调用
起因
最近碰到好几起开发人员被API/框架绑架的情形,比如想调用某个第三方库的API,而部分参数还没有准备好,然后拐着弯的写功能来确保调用时这些参数都同时准备好。这样就导致了即便这个API不是侵入式的,但是开发人员按照API来设计代码,使得达到了侵入式的后果…先同情下这样的朋友。
由此事件联想到了好几年前学习函数式编程时所产生的冲击,尤其是柯里化(Currying)。柯里化可以让多参数函数在调用时每次调用传递一个参数,例如以下的javascript代码:
//正常的调用
test(1, 2, 3, 4)
//柯里化调用1
test(1)(2)(3)(4)
//柯里化调用2
let fun = test(1)(2)
....//中间执行若干操作
fun(3)(4)
但是,为现有的方法添加柯里化风格的支持还是有一定工作量的。当然,这类问题用适配器模式和建造者模式来解决也是不错的方法,但是开发一些临时的建造者也挺麻烦(手敲累,会多出很多类)。这篇文档就提供一种简单、通用且有趣的方式来处理这类问题。
经过
代码很少,直接上代码。(懒得看的,直接到下方看结果)
/* 函数式接口,这里只考虑5个参数以下且有返回值的调用,10个参数类似,无返回值就用Consumer */
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
@FunctionalInterface
interface BiFunction<A, B, V> {
V apply(A a, B b);
}
@FunctionalInterface
interface Function3<A, B, C, V>{
V apply(A a, B b, C c);
}
@FunctionalInterface
interface Function4<A, B, C, D, V>{
V apply(A a, B b, C c, D d);
}
@FunctionalInterface
interface Function5<A, B, C, D, E, V>{
V apply(A a, B b, C c, D d, E e);
}
}
/* 核心类,30行代码把方法包装成柯里化风格,Result只是用来处理异常用的(参考另一篇文章),不需要的可以直接去掉 */
public class Invoker<T, R> {
private final Function<T, R> function;
public Invoker(Function<T, R> function) {
this.function = function;
}
public static <A, V> Invoker<A, Result<V>> invoke(Function<A, V> function) {
return new Invoker<>((A a) -> Result.from(() -> function.apply(a)));
}
public static <A, B, V> Invoker<A, Invoker<B, Result<V>>> invoke(BiFunction<A, B, V> function) {
return new Invoker<>((A a) -> invoke(b -> function.apply(a, b)));
}
public static <A, B, C, V> Invoker<A, Invoker<B, Invoker<C, Result<V>>>> invoke(Function.Function3<A, B, C, V> function) {
return new Invoker<>((A a) -> invoke((B b, C c) -> function.apply(a, b, c)));
}
public static <A, B, C, D, V> Invoker<A, Invoker<B, Invoker<C, Invoker<D, Result<V>>>>> invoke(Function.Function4<A, B, C, D, V> function) {
return new Invoker<>((A a) -> invoke((B b, C c, D d) -> function.apply(a, b, c, d)));
}
public static <A, B, C, D, E, V> Invoker<A, Invoker<B, Invoker<C, Invoker<D, Invoker<E, Result<V>>>>>> invoke(Function.Function5<A, B, C, D, E, V> function) {
return new Invoker<>((A a) -> invoke((B b, C c, D d, E e) -> function.apply(a, b, c, d, e)));
}
public R apply(T t) {
return function.apply(t);
}
}
结果
下面是调用示例:
public class Test{
private static int test1(String i) {
return Integer.parseInt(i);
}
private static String test2(int i, String s) {
return s + " => " + i;
}
private static String test3(int i, int j, String s) {
return s + " -> " + i + " - " + j;
}
private static String test4(int i, int j, String s, Object o) {
return s + " -> " + i + " - " + j + " - " + o;
}
public static void main(String[] args){
//单参数调用
int i = Invoker.invoke(Test::test1).apply("1").get();
//2个参数的调用
String s = Invoker.invoke(Test::test2).apply(1).apply("asdf").get();
//3个参数的调用
String s1 = Invoker.invoke(Test::test3).apply(1).apply(2).apply("").get();
//4个参数的调用
String ss = Invoker.invoke(Test::test4).apply(1).apply(2).apply(null).apply(new boolean[1]).get();
}
}