将函数和参数传递给另一个函数

时间:2019-05-23 09:36:57

标签: java lambda java-8

我有一些功能在catch块中执行相同的操作。

private void fun1(int a){
  try{
    // do api calls
  }catch(Exception e){
    refreshToken();
    fun1(a);
  }
}

private int fun2(int a, String b){
  try{
    // do api calls
  }catch(Exception e){
    refreshToken();
    fun2(a,b);
  }
}

private void fun3(String a, long b, char c){
  try{
    // do api calls
  }catch(Exception e){
    refreshToken();
    fun3(a,b,c);
  }
}

在这里,当应用程序令牌到期时,我需要调用一个函数来获取新令牌,并需要调用父函数。

在catch块中,代码被重复。所以我想在一个功能中执行这些操作。为此,我需要传递函数和参数。是否可以在Java中做到这一点?

5 个答案:

答案 0 :(得分:2)

如果您更频繁地需要这种功能,则可能需要构建自己的支持实用程序(然后您还可以根据需要对其进行改进),例如:

public class Retry {
  public static <T> T endlessTryCall(Callable<T> callable, Consumer<Exception> exceptionHandler) {
    for(;;) try {
        return callable.call();
      } catch (Exception e) {
        exceptionHandler.accept(e);
      }
  }

  public static void endlessTryRun(Runnable runnable, Consumer<Exception> exceptionHandler) {
    for(;;) try {
        runnable.run();
        return;
      } catch (Exception e) {
        exceptionHandler.accept(e);
      }
  }
}

您的函数将如下所示:

private void fun1(int a){
  Retry.endlessTryRun(() -> {
    // the API calls
  }, e -> refreshToken());
}

private int fun2(int a, String b){
  return Retry.endlessTryCall(() -> {
    // the API calls
    return ...;
  }, e -> refreshToken());
}

private void fun3(String a, long b, char c){
  Retry.endlessTryRun(() -> {
    // the API calls
    }, e -> refreshToken());
}

其他改进可以是使用tryCall(numberOfTrials, ...)函数之类的功能,或者是另一个可以按您所需方式处理异常的函数。演示后者:

private void fun1(int a){
  refreshTokenOnException(() -> {
    // the API calls
  });
}

private int fun2(int a, String b){
  return refreshTokenOnException(() -> {
    // the API calls
    return ...;
  });
}

private void fun3(String a, long b, char c){
  refreshTokenOnException(() -> {
    // the API calls
  });
}

private <T> T refreshTokenOnException(Callable<T> callable) {
  return Retry.endlessTryCall(callable, e -> refreshToken());
}
private void refreshTokenOnException(Runnable runnable) {
  Retry.endlessTryRun(runnable, e -> refreshToken());
}

使用这种实用程序的好处可能令人怀疑。我认为所做的工作有些冗长。但是话又说回来,必须为其找到合适的名称……所以……这依旧取决于。

答案 1 :(得分:1)

您在这里已经做过最合理的事情,将通用代码提取到方法refreshToken()中,并在必要时在每个位置调用此方法。这就是我们的操作方式,并且可以接受单个方法调用的“代码重复”。将代码分解成更复杂的内容并不是很有建设性的,即使每种情况下复杂的代码由很多不同的样板组成。

您唯一应该避免的事情是通过递归重复该操作。由于当前令牌的失效可能会发生任意次数,因此,由于您无法控制评估所需的时间,因此可能会有任意次数的递归,从而增加了StackOverflowError的风险。同样,递归调用在这里确实是可以避免的代码。
只需使用循环即可。

private void fun1(final int a) {
  for(;;) try {
    // do api calls, don't modify a
    return;
  } catch(Exception e) {
    refreshToken();
  }
}

private int fun2(final int a, final String b) {
  try {
    // do api calls, don't modify a nor b
    return result;
  } catch(Exception e) {
    refreshToken();
  }
}

private void fun3(final String a, final long b, final char c) {
  for(;;) try {
    // do api calls, don't modify a, b, nor c
    return;
  } catch(Exception e) {
    refreshToken();
  }
}

答案 2 :(得分:0)

您可以采取解决方法,并执行“ switch ... case”操作,并将switch键作为参数...

<kotlin.version>1.3.30</kotlin.version>
<spring.version>5.1.4.RELEASE</spring.version>

} }

您将需要输入所有3个函数的所有参数作为参数...

另一个选择是使用Java反射

答案 3 :(得分:0)

package be.test;

public class FuncTest {

    private void fun1(int a) {
        // do api calls
    }

    private int fun2(int a, String b) {
        // do api calls
        return 0;
    }

    private void fun3(String a, long b, char c) {
        // do api calls
    }

    private void refreshToken() {
        // do your refresh token logic
    }

    // functional interface
    public interface MyFunc {
        public void doIt();
    }

    public void callWithRefresh(MyFunc func)
    {
        try {
            func.doIt();
        } catch (Exception e) {
            refreshToken();
            func.doIt();
        }
    }
    public static void main(String[] args) {
        FuncTest ftest=new FuncTest();
        ftest.callWithRefresh(()->ftest.fun1(1));
        ftest.callWithRefresh(()->ftest.fun2(1,"hopla"));
        ftest.callWithRefresh(()->ftest.fun3("hopla",1,'c'));
    }
}

callWithRefresh包含在发生异常时调用refreshToken的通用逻辑。 (希望异常原因与令牌过期有关:)

MyFunc只是一个能够调用doIt()的接口。不必在任何地方实现此接口。

答案 4 :(得分:0)

考虑使用Failsafe之类的库,该库在处理不同的错误情况时具有更大的灵活性。

首先,您必须声明一个重试策略,该策略定义应处理哪些故障以及何时应执行重试。您只想在令牌过期时重试,而不是在引发另一个错误时重试。

RetryPolicy<Object> retryPolicy = new RetryPolicy<>()
            .handle(TokenExpiredException.class)
            .withMaxRetries(3);

并按以下方式调用

Failsafe.with(retryPolicy)
            .onFailure(e -> refreshToken())
            .run(() -> fun1(42));

此外,它可以与(经检查的)供应商一起使用,就像您使用fun2时一样(它返回一个整数):

int result = Failsafe.with(retryPolicy)
                .onFailure(e -> refreshToken())
                .get(() -> fun2(12, "cat"));