递归调用一个方法但只记录第二次?

recursively call a method but log only second time?

我有一个下面的方法,它对使用字符集传递的值进行 URL 解码。

  public String decodeValue(String value, Charset charset) {
    if (!Strings.isNullOrEmpty(value)) {
      try {
        value = URLDecoder.decode(value, charset.name());
      } catch (UnsupportedEncodingException ex) {
        // log error
        return null;
      }
    }
    return value;
  }

现在,如果 URLDecoder.decode 行第一次抛出 UnsupportedEncodingException 那么我想 运行 相同 value 针对以下三行:

value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
value = value.replaceAll("\+", "%2B");
value = URLDecoder.decode(value, charset.name());

如果 URLDecoder.decode 行第二次抛出异常,那么我将记录错误但仅第二次和 return 空值,否则 return 解码的值。

执行此操作的最佳且优雅的方法是什么?

最简单的方法是制作包含额外标志的函数签名的私有版本。

  private String decodeValue(String value, Charset charset, boolean isFirstTime) {
    if (!Strings.isNullOrEmpty(value)) {
      try {
        value = URLDecoder.decode(value, charset.name());
      } catch (UnsupportedEncodingException ex) {
        if (isFirstTime) {
            value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
            value = value.replaceAll("\+", "%2B");
            return decodeValue(value, charset.name(), false);
        } else {
            // log error
            return null;
        }
      }
    }
    return value;
  }

然后,第一次传true,递归调用false就可以了。在函数内部,如果传递 true 则只执行接下来的三行。

public版本可以通过true.

  public String decodeValue(String value, Charset charset) {
       decodeValue(value, charset, true);
  }

给你:

 public String decode(String value, Charset charset) {
    if (!Strings.isNullOrEmpty(value)) {
      try {
        value = URLDecoder.decode(value, charset.name());
      } catch (UnsupportedEncodingException ex) {
        try {
        value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
        value = value.replaceAll("\+", "%2B");
        value = URLDecoder.decode(value, charset.name());
      } catch (UnsupportedEncodingException ex) {
        // log error
        return null;
      }
      }
    }
    return value;
  }

希望这能解决您的问题。

添加标志是一个选项。是的,这是比较容易的;但很多人认为拥有 标志 只是口语:不好的做法。

你只是尽量减少这些事情。

换句话说:如果您有一个方法在第一次调用和后续调用中表现不同;考虑在那里创建 两个 方法。当然,您会尽可能避免代码重复,但除非它变得代价高昂,否则您至少应该考虑避免此类标志参数。

虽然它不是递归的,但您可以使用 while 循环和标志。

public String decode(String value, Charset charset) {
    boolean first = true;
    while(!Strings.isNullOrEmpty(value)) {
        try {
            return value = URLDecoder.decode(value, charset);
        } catch (UnsupportedEncodingException e) {
            if(first == false) {
                // Log error.
                return null;
            }
            value = value.replaceAll("%(?![0-9a-fA-F]{2})", "%25").replaceAll("\+", "%2B");
        }
        first = false;
    }
    return value;
}

这是没有额外标志、重复代码、递归和循环的版本:

public String decodeValue(final String value, Charset charset) throws UnsupportedEncodingException {
    String result;
    if (!Strings.isNullOrEmpty(value)) {
        UnsupportedEncodingException[] lastException = new UnsupportedEncodingException[1];
        result = Stream.<Function<String, String>>of(
                Function.identity(),
                s -> {
                    s = s.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
                    s = s.replaceAll("\+", "%2B");
                    return s;
                })
                .map(f -> f.apply(value))
                .map(appliedValue -> {
                    try {
                        return URLDecoder.decode(appliedValue, charset.name());
                    } catch (UnsupportedEncodingException e) {
                        lastException[0] = e;
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .findFirst()
                .orElseThrow(() -> lastException[0]);
    }
    return result;
}

这里我只使用 2 个函数进行流处理:标识和进行字符串更正的函数。然后我将这些函数应用于初始字符串并尝试解码。如果第一次尝试成功,则代码将不会应用更正功能,而只是 return 正确的结果。如果解码器在值校正后抛出异常,则 "findFirst" 将找不到任何值。然后我们抛出最后捕获的异常。