多功能组合

ftf50wuq  于 2021-07-06  发布在  Java
关注(0)|答案(2)|浏览(248)

今天我遇到了下面一个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 方法,因此方法重载不是一个选项。
这种方法应该返回链中最后一个函数的结果,而不需要显式转换。

bjp0bcyl

bjp0bcyl1#

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

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);
    }

}

我一点也不满意。它允许使用vararg参数,但是使用原始的、未参数化的参数 Function 毫无意义。nikolas charalambidis的答案是一个更好的解决方案,因为我们保留了返回类型信息和安全性。

qlfbtfca

qlfbtfca2#

问题
您需要为每个数量的预期函数链接泛型,并使用五个函数链接下面代码段中的泛型参数:

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

但是,对于未知数量的参数(varargs),这是不可能的。没有“vargenerics”这样的东西可以像上面那样动态地创建和链接泛型参数。
解决方案
你可以改为治疗 InputConverter 作为一个建设者而不是返回自我与每个 convertBy 呼叫并最终打包结果。这种递归行为允许无限次的调用。试一试:

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

相关问题