Using Java 8 Lambdas with Generics

Is it possible to do this using the Predicate interface.

I have a client class that uses the functions provided by the MathUtility class. Regardless of the Mathmatical operation, this should only happen in the MathUtility class.

    //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
    }

It does not currently work with this error - The method reduce(T, BinaryOperator<T>) in the type Stream<T> is not applicable for the arguments (int, T::sum)

Note. I do not want to write sum functions for different types of numbers

+4
source share
4 answers

Is there a way to do this without writing a sum function for all the possible types Tthat I expect?

As Aaron Davis said in the comment above, you can pass recovery options 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);
}

Example:

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
+5
  • , Number , Number .
  • identity T extends Number, 0 - 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);

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

, .

It should be noted that the add logic is not executed on the caller, and not just in MathUtility. The disadvantage here is that you need to create add classes for each type of number in which the + operation is required.

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
source

Answer: yes, it should be possible. It is not known that you defined the method "sum", so the compiler complains. Try to identify

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

(I have not tried this code in the IDE, but this should do the trick)

-2
source

Source: https://habr.com/ru/post/1674885/


All Articles