【问题标题】:Create a method that accepts variable length of Function arguments with possibly different types创建一个接受可变长度的函数参数的方法,该参数可能具有不同的类型
【发布时间】:2016-01-20 11:15:57
【问题描述】:

假设我有一个字符串:String s = "1,2,3,4,5,6"。我想创建一个方法combineFunctions(),它将Functions 的可变长度序列作为参数,并按该顺序应用所有操作。

函数可能有不同的<T,U> 类型。

此类函数的示例用法如下:

Combine<String> c = new Combine<>(s);
List<String> numbers = c.combineFunctions(splitByComma);
Integer max = c.combineFunctions(splitByComma,convertToInt, findMax);

我尝试过的(这里的&lt;U&gt;在这里用处不大):

public <U> void combineFunctions(
        Function<? extends Object, ? extends Object>... functions) {

}

但我一直在获取Functions 的最后一个类型。我也在考虑一种递归方法,但 varargs 参数必须是最后一个。

这种方法可以用Java实现吗?

【问题讨论】:

  • 你说的“但我被困在获取最后一个函数的类型”是什么意思。你能解释一下吗?
  • 看起来像 Streammap 方法。
  • 如果你知道它总是会使用相对较少的参数(比如少于 10 个函数)被调用,你可以编写 10 个重载的combine(Function&lt;T, T1&gt; Function&lt;T1, T2&gt;, ..., Function&lt;Tn, U&gt;) 方法。这是在接受各种(但有限)数量的参数的同时保持类型安全的标准方法。

标签: java lambda functional-programming java-8


【解决方案1】:

这样一个函数的问题是你失去了所有编译时类型检查和强制转换是必要的。

这将是一个实现,使用andThen 将功能组合在一起。由于所有的演员阵容,这看起来很难看,我不确定你能不能做得更好。另请注意,当真正需要 1 个时,这需要创建 2 个 Stream 管道。

public static void main(String[] args) throws Exception {
    String str = "1,2,3,4,5,6";
    Function<Object, Object> splitByComma = s -> ((String) s).split(",");
    Function<Object, Object> convertToInt = tokens -> Stream.of((String[]) tokens).map(Integer::valueOf).toArray(Integer[]::new);
    Function<Object, Object> findMax = ints -> Stream.of((Integer[]) ints).max(Integer::compare).get();
    Integer max = (Integer) combineFunctions(splitByComma, convertToInt, findMax).apply(str);
    System.out.println(max);
}

@SafeVarargs
private static Function<Object, Object> combineFunctions(Function<Object, Object>... functions) {
    return Arrays.stream(functions)
                 .reduce(Function::andThen)
                 .orElseThrow(() -> new IllegalArgumentException("No functions to combine"));
}

要匹配您问题中的代码,您可以将其包装到这样的类中:

public class Combiner<R> {

    private Object input;

    public Combiner(Object input) {
        this.input = input;
    }

    @SuppressWarnings("unchecked")
    @SafeVarargs
    public final R combineFunctions(Function<Object, Object>... functions) {
        return (R) Arrays.stream(functions)
                         .reduce(Function::andThen)
                         .orElseThrow(() -> new IllegalArgumentException("No functions to combine"))
                         .apply(input);
    }

}

【讨论】:

  • 我很抱歉,但这是我的错误 - 该方法不必是静态的,我认为实际上不可能以我的方式实现 static 方法在我的问题中展示(编辑了问题)。看起来您的提示将引导我找到正确的解决方案。
  • @REACHUS 这只是一个示例代码。方法的代码当然可以放在一个类中并在非静态上下文中调用,请参阅我的编辑。
  • @REACHUS:方法是否为static 无关紧要。使用 varargs 方法无法通过类型安全来支持这一点。唯一的方法是使用构建器模式,但这正是 map 链(在流上或可选)或在函数调用上重复 andThen 已经做的事情。事实上,您的Combiner 几乎是Optional...
  • @REACHUS 否,仍然创建了 2 个流:convertToInt 中的一个和 findMax 中的一个
  • 如果你接受缺乏类型安全,你可以像Object o=input; for(Function f: functions) o=f.apply(o); return o;一样实现combineFunctions方法,但目前还不清楚你为什么认为你需要这个……
【解决方案2】:

使用函数式Streams 可以很容易地解决您问题中的示例。解决此问题的“功能”方法是使用map 操作序列,每个步骤将元素转换为不同的类型,然后可选地减少/收集结果。

例如

String str = "1,2,3,4,5,6,7";
int max = Arrays.stream(str.split(",")).mapToInt(Integer::parseInt).max().orElse(0)

同样的模式也适用于其他类型的“功能组合”。

【讨论】:

    猜你喜欢
    • 2012-12-25
    • 2019-03-01
    • 2020-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-14
    • 2013-09-19
    相关资源
    最近更新 更多