【问题标题】:Performing same operation on multiple functions对多个函数执行相同的操作
【发布时间】:2019-04-05 13:54:25
【问题描述】:

我有一个包含多个get 函数的类,例如getF1getF10。对于这些吸气剂中的每一个,我希望将字母 "x" 替换为 "a"(随机示例)。 getter 可以返回一个空值。

到目前为止,这就是我所做的并且它有效,有没有办法得到比这更好看的东西?

public void foo(final MyObject bar) {
    Optional.of(bar).map(MyObject::getF1).ifPresent(s -> bar.setF1(s.replaceAll("x", "a"));
    Optional.of(bar).map(MyObject::getF2).ifPresent(s -> bar.setF2(s.replaceAll("x", "a")));
    ...
    Optional.of(bar).map(MyObject::getF10).ifPresent(s -> bar.setF10(s.replaceAll("x", "a")));
}

我在想这样的事情,使用列表,显然,这段代码是错误的,但你明白了:

public void foo(final MyObject bar) {
    List<Function> func = new ArrayList<Function>();
    func.addAll(Arrays.asList(MyObject::getF1, MyObject::getF2, ..., MyObject::getF10));
    Optional.of(bar).map(func).ifPresent(s -> func(s.replaceAll("x", "a"));
}

也许使用流可以完成工作?

谢谢!

【问题讨论】:

  • 这些字段是F1 ... F10 生成的还是您手动创建的?
  • 第三行 Optional.of(bar).map(MyObject::getF10).ifPresent(s -&gt; bar.setF3(s.replaceAll("x", "a"))); - 正在获取 F10 并设置 F3 是故意还是错字?
  • 如果 F1 - F10 属于同一类型,您能否将它们作为数组存储在您的对象中,并创建一个通用的 get() 方法,在其中指定索引 1-10。这样你就可以在 for 循环中遍历并对每一个执行相同的操作。
  • @Nikolas 确实是一个错字
  • @Nikolas F1 到 F10 是用户输入生成的字符串

标签: java dictionary java-8 optional


【解决方案1】:

您可以将Optional::map 中使用的映射器和Optional::ifPresent 中使用的消费者存储在Map 中。

我还建议您创建一个方法来避免字符串替换的代码重复,该方法应该很容易调用。这很有用,因为所有替换都是相同的

private String replaced(String string) {
    return string.replaceAll("x", "a");
}

然后简单地遍历条目并应用每个键值对(顺序无关紧要):

Map<Function<? super MyObject, ? extends String>, Consumer<? super String>> map = new HashMap<>();
map.put(MyObject::getF1, bar::setF1);
map.put(MyObject::getF2, bar::setF2);
map.put(MyObject::getF10, bar::setF10);
// ...

map.forEach((function, consumer) -> {
        Optional.of(bar).map(function).map(this::replaced).ifPresent(consumer);
});

如果你想扩展这个机制并对传递给setter的每个String应用不同的函数,那么你还需要使用不同的结构:

public final class Mapping {

    private final Function<MyObject, String> getterFunction;
    private final Function<String, String> transformationFunction;
    private final Consumer<String> setterFunction;

    public Mapping(final Function<MyObject, String> getterFunction, final Function<String, String> transformationFunction,
        final Consumer<String> setterFunction) {
        this.getterFunction = getterFunction;
        this.transformationFunction = transformationFunction;
        this.setterFunction = setterFunction;
    }

    // getters
}

并且用法类似(转换函数是样本,可能会有所不同):

List<Mapping> list = new ArrayList<>();
list.add(new Mapping(MyObject::getF1, s -> s.replaceAll("x", "a"), bar::setF1));
list.add(new Mapping(MyObject::getF2, s -> s.replaceAll("x", "a"), bar::setF2));
list.add(new Mapping(MyObject::getF10, s -> s.replaceAll("x", "a"), bar::setF10));

list.forEach(mapping -> {
    Optional.of(bar)
            .map(mapping.getGtterFunction)
            .map(mapping.getTransformationFunction)
            .ifPresent(mapping.getSetterFunction);
});

【讨论】:

  • Optional.map(Optional.java:215) 上获得 NPE,线路是 return Optional.ofNullable(mapper.apply(value));。不知道为什么, Optional 应该处理空值?此外,我的测试中没有任何 null。
  • Optional 在 null 结果之后处理。如果映射器本身为空,则抛出 NPE。我无法从这里调试它。调试您的代码并检测失败的原因,并最终向我提供您的代码的链接(不要在此处复制完整的代码),我来看看。
  • 好的,剩下的我想我可以搞定,非常感谢您的帮助和解释。
  • 我有 NPE,因为我在另一个类中写了 replaced 并用 @Inject 调用它。我必须手动实例化它。
【解决方案2】:

您可以将 getter 和 setter 配对为 Suppliers 和 Consumers:

public void foo(final MyObject bar) {

    if (bar == null)
        return;

    Map<Supplier<String>, Consumer<String>> funcs = new HashMap<>();
    funcs.put(bar::getF1, bar::setF1);
    funcs.put(bar::getF2, bar::setF2);

    funcs.forEach(
            (getter, setter) -> Optional.of(getter.get()).ifPresent(s -> setter.accept(s.replaceAll("x", "a"))));
}

还请注意,Optional 处理的 null 参数已被保护子句取代:这必须在解析 bar::... 之前出现,以防止 NPE。它还使预期的null 处理更加清晰。

【讨论】:

  • 这与我的回答没有什么不同,而且s.replaceAll("x", "a") 是固定的,无法应用每个字段的单独转换。
  • @Nikolas,我不同意:虽然使用 Map 来配对功能很相似,但我的回答 1)在 bar 对象上使用 Suppliers 和 2)记下NPE 的危险。所以,确实有区别。 (顺便说一句,每个字段单独不同的转换不在原始问题中。)
  • 无声无息地返回不符合fail-fast原则,这是错误的,会导致隐藏的bug。
  • 感谢您指出这一点。虽然在出现错误情况时肯定需要快速失败,但这似乎不适用于此处:原始问题中的显式Optional.of(bar) 表明1)null 是一个有效参数,2)什么都不应该做在这种情况下。保护子句只是使这些语义明确。我已经编辑了答案以使其更清楚。
猜你喜欢
  • 1970-01-01
  • 2021-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-29
  • 1970-01-01
  • 2012-01-05
相关资源
最近更新 更多