将Java 8 Lambdas与泛型一起使用

时间:2017-04-15 17:22:45

标签: java generics lambda java-8

是否可以使用Predicate接口执行此操作。

我有一个使用MathUtility类提供的函数的客户端类。无论Mathmatical操作是什么,它都应该只在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)

注意:我不想为不同的数字类型编写和函数

4 个答案:

答案 0 :(得分:5)

  

有没有办法在没有为我期待的每种可能类型的T编写求和函数的情况下这样做?

正如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 :(得分:2)

  1. 您必须指出要汇总的Number的实际类型,因为Number类没有静态求和方法。
  2. 您必须为identity分配T extends Number类型,0是具体类型的整数,并且与T的类型不兼容。
  3. 可能的解决方案

    您可以在以后汇总哪个实际类型的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);
    

    MathUtility

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

    SumOp

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

答案 2 :(得分:1)

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

需要注意的是,加法逻辑不是在调用端发生,而是仅在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);
    }

}

答案 3 :(得分:-2)

答案是肯定的,这应该是可能的。你定义的那个不知道有方法&#34; sum&#34;因此编译器会抱怨。尝试定义

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

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