【问题标题】:Multiple function composition多功能组合
【发布时间】:2021-03-03 08:44:39
【问题描述】:

今天我遇到了以下 Java 作业,但我不知道如何通过类型擦除。

任务是创建一个通用 InputConverter 类,该类接受 T 类型的输入并使用作为方法参数接收的多个函数链对其进行转换。它必须支持以下符号:

Function<String, List<String>> lambda1 = ...;
Function<List<String>, String> lambda2 = ...;
Function<String, Integer> lambda3 = ...;

String input = ...;

List<String> res1 = new InputConverter(input).convertBy(lambda1);

Integer res2 = new InputConverter(input).convertBy(lambda1, lambda2, lambda3);

这是我想出的:

import java.util.Arrays;
import java.util.function.Function;

public class InputConverter<T> {
    private final T input;

    public InputConverter(T input) {
        this.input = input;
    }

    public <B> B convertBy(Function<T, ?> first, Function<?, ?>... functions) {
        var res = first.apply(input);

        Function<?, B> composed = Arrays.stream(functions)
            .reduce(Function::andThen)
            .orElse(Function.identity());

        return composed.apply(res);
    }

}

这当然行不通,因为我找不到确定最后一个函数的返回类型的方法。

注意事项:

  • InputConverter 应该只定义一个convertBy 方法,因此方法重载不是一种选择。
  • 此方法应返回链中最后一个函数的结果,无需显式强制转换。

【问题讨论】:

  • 你确定分配有任意数量的函数作为参数吗? (如果是固定数字,当然容易多了)

标签: java generics java-8 functional-programming type-erasure


【解决方案1】:

因此,根据任务的作者,正确的解决方案是:

import java.util.Arrays;
import java.util.function.Function;

public class InputConverter<T> {
    private final T input;

    public InputConverter(T input) {
        this.input = input;
    }

    public <B> B convertBy(Function<T, ?> first, Function... functions) {
        var res = first.apply(input);

        Function<Object, B> composed = Arrays.stream(functions)
            .reduce(Function::andThen)
            .orElse(Function.identity());

        return composed.apply(res);
    }

}

这对我来说根本不满意。它允许使用可变参数,但使用原始的、未参数化的 Function 毫无意义。 Nikolas Charalambidis' answer 是一个更好的解决方案,因为我们保留了返回类型信息和安全性。

【讨论】:

  • 嘿,这很好!我完全忘记了原始参数。让我 +1 来帮助 OP。
【解决方案2】:

问题

您需要为每个数量的预期函数链接泛型,并将泛型参数链接到下面的 sn-p 中,其中包含五个函数:

public <D> E convertBy(
    Function<T, A> first, Function<A, B> second, Function<B, C> third, 
    Function<C, D> fourth, Function<D, E> fifth) {
    ...
}

但是,对于未知数量的参数(可变参数),这是不可能的。没有像“vargenerics”这样的东西,它会像上面那样动态创建和链接泛型参数。


解决方案

您可以将InputConverter 视为一个builder,而不是在每个convertBy 调用中返回self,最后打包一个结果。这种递归行为允许无限数量的调用。试试看:

public static class InputConverter<T> {

    private final T data;

    public InputConverter(T data) {
        this.data = data;
    }

    public <U> InputConverter<U> convertBy(Function<T, U> function) {
        return new InputConverter<>(function.apply(data));
    }

    public T pack() {
        return data;
    }
}

很整洁,不是吗?让我们看看最小样本的用法:

// let lambda1 split String by characters and create a List
Function<String, List<String>> lambda1 = str -> Arrays.stream(str.split(""))
                                                      .collect(Collectors.toList());
// let lambda2 get the first item
Function<List<String>, String> lambda2 = list -> list.get(0);

// let lambda3 parse a String into the Integer
Function<String, Integer> lambda3 = Integer::parseInt;

String input = "123";                                   // out sample numeric input

List<String> res1 = new InputConverter<String>(input)   // don't forget the generics
    .convertBy(lambda1)
    .pack();

Integer res2 = new InputConverter<String>(input)
    .convertBy(lambda1)
    .convertBy(lambda2)
    .convertBy(lambda3)
    .pack();

System.out.println(res1);                               // [1, 2, 3]
System.out.println(res2);                               // 1

【讨论】:

  • 我一开始也是这么想的,但任务明确指出 convertBy 应该像我描述的那样工作。我猜作者没有检查是否可以完成
  • 这是什么Java任务分配?如果它来自老师,我也想学习如何实现它。如果它是个人任务或工作任务,您最好确信在这样的限制下没有解决方案(至少我不知道有任何解决方案)。不过,这个问题本身就很有趣。
  • 这是我朋友的学校作业。如果有的话我会在这里发布答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多