执行map-reduce操作的通用方法.(爪哇-8)

Kei*_*ATO 8 java generics overloading function java-8

如何在Java 8中使用泛型参数重载函数?

public class Test<T> {

    List<T> list = new ArrayList<>();

    public int sum(Function<T, Integer> function) {
        return list.stream().map(function).reduce(Integer::sum).get();
    }


    public double sum(Function<T, Double> function) {
        return list.stream().map(function).reduce(Double::sum).get();
    }
}
Run Code Online (Sandbox Code Playgroud)

错误:java:name clash:sum(java.util.function.Function <T,java.lang.Double>)和sum(java.util.function.Function <T,java.lang.Integer>)具有相同的擦除

srb*_*gan 6

Benji Weber曾写过一种规避这种方法的方法.您需要做的是定义扩展参数类型的自定义功能接口:

public class Test<T> {

    List<T> list = new ArrayList<>();

    @FunctionalInterface
    public interface ToIntFunction extends Function<T, Integer>{}
    public int sum(ToIntegerFunction function) {
        return list.stream().map(function).reduce(Integer::sum).get();
    }


    @FunctionalInterface
    public interface ToDoubleFunction extends Function<T, Double>{}
    public double sum(ToDoubleFunction function) {
        return list.stream().map(function).reduce(Double::sum).get();
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用java.util.function.ToIntFunction和java.util.function.ToDoubleFunction:

public class Test<T> {

    List<T> list = new ArrayList<>();

    @FunctionalInterface
    public int sum(ToIntFunction function) {
        return list.stream().mapToInt(function).sum();
    }

    public double sum(ToDoubleFunction function) {
        return list.stream().mapToDouble(function).sum();
    }
}
Run Code Online (Sandbox Code Playgroud)


CKi*_*ing 3

您在问题中提供的示例与 Java 8 无关,而与泛型在 Java 中的工作方式有关。Function<T, Integer> function并在编译时Function<T, Double> function进行类型擦除并转换为Function. 方法重载的经验法则是具有不同数量、类型或顺序的参数。由于您的两个方法都会转换为接受Function参数,因此编译器会对此进行抱怨。

话虽如此,srborlongan已经提供了一种解决该问题的方法。该解决方案的问题在于,您必须Test针对不同类型(整数、双精度等)上的每种类型的操作(加法、减法等)不断修改类。另一种解决方案是使用method overriding而不是method overloading

Test稍微改变一下类,如下:

public abstract class Test<I,O extends Number> {

    List<I> list = new ArrayList<>();

    public O performOperation(Function<I,O> function) {
        return list.stream().map(function).reduce((a,b)->operation(a,b)).get();
    }

    public void add(I i) {
        list.add(i);
    }

    public abstract O operation(O a,O b);
}
Run Code Online (Sandbox Code Playgroud)

创建一个子类Test,将添加两个Integers。

public class MapStringToIntAddtionOperation extends Test<String,Integer> {

    @Override
    public Integer operation(Integer a,Integer b) {
        return a+b;
    }

}
Run Code Online (Sandbox Code Playgroud)

然后客户端代码可以使用上面的代码,如下所示:

public static void main(String []args) {
    Test<String,Integer> test = new MapStringToIntAddtionOperation();
    test.add("1");
    test.add("2");
    System.out.println(test.performOperation(Integer::parseInt));
}
Run Code Online (Sandbox Code Playgroud)

使用这种方式的好处是你的Test类是符合open-closed原则的。要添加新的运算(例如乘法),您所要做的就是添加一个新的子类Test以及将两个数字相乘的方法overrideoperation将其与装饰器模式结合起来,您甚至可以最大限度地减少必须创建的子类的数量。

注意此答案中的示例是指示性的。有很多改进的领域(例如制作Test函数接口而不是抽象类)超出了问题的范围。