繁体   English   中英

将 Java 8 Lambda 与泛型结合使用

[英]Use of Java 8 Lambdas with Generics

是否可以使用 Predicate 接口来做到这一点。

我有一个使用 MathUtility 类提供的函数的客户端类。 无论是什么数学运算,它都应该只在 MathUtility 类中发生。

    //in client 
    MathUtility.sum(listOfInts, (Integer i)->{return (i<3);});

   //in utility
    class MathUtility<T extends Number> {
        public static <T extends Number> T sumWithCondition(List<T> numbers, Predicate<T> condition) {
            return numbers.parallelStream()
                    .filter(condition)
                    .map(i -> i)
                    .reduce(0, T::sum); //compile time error
        }
        public static <T extends Number> T avgWithCondition(List<T> numbers, Predicate<T> condition) {
            //another function
        }
        //lot many functions go here
    }

现在它因此错误而失败 - The method reduce(T, BinaryOperator<T>) in the type Stream<T> is not applicable for the arguments (int, T::sum)

注意:我不想为不同的 Number 类型编写 sum 函数

编辑:此Github Notebook 中涵盖的有关此主题的详细讨论

有没有办法在不为我期望的每种可能类型的T编写 sum 函数的情况下做到这一点?

正如 Aaron Davis 在上面的评论中所述,您可以将缩减参数传递给方法本身。

public static <T> T sumWithCondition(List<T> numbers, Predicate<T> condition, T identity, BinaryOperator<T> accumulator) {
    return numbers.parallelStream().filter(condition).reduce(identity, accumulator);
}

一个例子是:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

System.out.println(sumWithCondition(list, i -> i > 1, 0, (a, b) -> a + b));

>> 14

List<BigInteger> list2 = Arrays.asList(BigInteger.ONE, BigInteger.ONE);

System.out.println(sumWithCondition(list2, i -> true, BigInteger.ZERO, (a, b) -> a.add(b)));

>> 2
  1. 您必须指出要求和的实际Number类型,因为Number类没有静态 sum 方法。
  2. 您必须为T extends Number类型分配identityT extends Number0是 Integer 的具体类型,与T类型不兼容。

可能的解决方案

您可以稍后对哪种实际类型的Number求和,例如:

Integer sumToInt = MathUtility.sum(numbers, condition).as(Integer.class);
Double sumToDouble = MathUtility.sum(numbers, condition).as(Double.class);

或者,您可以预先对哪种实际类型的Number进行求和,使用这种样式时,您可以自由地将实际Number类型用于要调用的每个总和,另一方面,您可以重用它而无需使用任何混淆的参数,并且正是你想要的,例如:

SumOp<Integer> sumIntOp = SumOp.of(Integer.class);

//sumIntOp is reused twice.
Integer sumToInt1 = sumIntOp.sum(numbers1, condition1);
Integer sumToInt2 = sumIntOp.sum(numbers2, condition2);

数学实用程序

class MathUtility {

    private static <T extends Number> Sum sum(List<T> numbers,
                                              Predicate<T> condition) {
        return sum(numbers.parallelStream().filter(condition));
    }

    private static <T extends Number> Sum sum(Stream<T> stream) {
        return new Sum() {
            public <T extends Number> T as(Class<T> type) {
                return SumOp.of(type).sum(stream);
            }
        };
    }

    interface Sum {
        <T extends Number> T as(Class<T> type);
    }
}

求和运算

public class SumOp<T extends Number> {
    private static final Map<Class<?>, SumOp<?>> OPERATORS = new HashMap<>();
    private final T identity;
    private final BinaryOperator<T> plusOp;
    private final Function<Number, T> valueExtractor;

    static {
       register(Integer.class, new SumOp<>(0, Integer::sum, Number::intValue));
       register(Double.class, new SumOp<>(0., Double::sum, Number::doubleValue));
       //todo: add more SumOp for other Number types
    }

    public static <T extends Number> void register(Class<T> type,
                                                   SumOp<T> sumOp) {
        OPERATORS.put(type, sumOp);
    }

    public static <T extends Number> SumOp<T> of(Class<T> type) {
        return (SumOp<T>) OPERATORS.computeIfAbsent(type, it -> {
            String message = "No SumOp registered for type:" + type.getName();
            throw new IllegalArgumentException(message);
        });
    }

    public SumOp(T identity,
                 BinaryOperator<T> plusOp,
                 Function<Number, T> valueExtractor) {
        this.identity = identity;
        this.valueExtractor = valueExtractor;
        this.plusOp = plusOp;
    }

    public <I extends Number> T sum(List<I> numbers,
                                    Predicate<I> condition) {
        return sum(numbers.stream().filter(condition));
    }

    public T sum(Stream<? extends Number> stream) {
        return stream.reduce(identity, this::plus, plusOp);
    }

    private T plus(Number augend, Number addend) {
        return plusOp.apply(valueIn(augend), valueIn(addend));
    }

    private T valueIn(Number it) {
        return valueExtractor.apply(it);
    }
}

我厌倦了一个更简单的方法是这个。

需要注意的一点是,加法逻辑不会发生在调用端,而是只发生在 MathUtility 中。 这里的缺点是您必须为每个您想要 + 操作的 Number 类型创建 Addition 类。

System.out.println(
                MathUtility.sum(listOfInts, i->i<4, new MathUtility.IntegerAddition()).get()
); 

class MathUtility<T extends Number> {

    static class IntegerAddition implements BinaryOperator<Integer> {

        @Override
        public Integer apply(Integer t, Integer u) {
            return t + u;
        }

    }


    public static <T extends Number> Optional<T> sum(List<T> list, Predicate<T> condition, BinaryOperator<T> operation){
        //ability to add is only here
            return list.parallelStream()
            .filter(condition)
            .map(i -> i)
            .reduce(operation);
    }

}

答案是肯定的,这应该是可能的。 不知道您定义的具有“sum”方法,因此编译器会抱怨。 尝试定义

public interace SumInterface {
   public int sum(int a, int b);
}

(我没有在 IDE 中尝试过这段代码,但这应该可以解决问题)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM