Use of Java 8 Lambdas with Generics

后端 未结 4 1798
执念已碎
执念已碎 2021-02-10 14:37

Is it possible to do this using Predicate interface.

I have a client class that utilizes functions provided by a MathUtility class. Whatever the Mathmatical operation it

相关标签:
4条回答
  • 2021-02-10 14:54
    1. you must point out which actual type of Number to be summed, Since the Number class has no static sum method.
    2. you must assign identity with type of T extends Number,0 is an concrete type of Integer and does not compatible with type of T.

    Possible Solution

    you can make which actual type of Number to be summed later, for example:

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

    OR you can make which actual type of Number to be summed ahead, when using this style you are free to take type of actual Number to every sum to be called, one the other hand, you can reuse it without taking any confused parameters and which is exactly what you want,for example:

    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);
        }
    }
    
    0 讨论(0)
  • 2021-02-10 14:54

    The answer is yes, that should be possible. The you defined is not known to have the method "sum", therefore the compiler complains. Try to define

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

    (I haven't tried this code in IDE but this should do the trick)

    0 讨论(0)
  • 2021-02-10 15:15

    A much simpler approach I tired is this.

    The point to be noted is that the addition logic doesn't happen at the invoking side instead only within the MathUtility. The downside here is that you have to create Addition classes for every Number type you want the + operation.

    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);
        }
    
    }
    
    0 讨论(0)
  • 2021-02-10 15:16

    Is there a way to do it without writing a sum function for every possible type of T that i'm expecting?

    As Aaron Davis stated in a comment above, you can pass the reduction parameters to the method itself.

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

    An example would be:

    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
    
    0 讨论(0)
提交回复
热议问题