简体   繁体   English

将 Java 8 Lambda 与泛型结合使用

[英]Use of Java 8 Lambdas with Generics

Is it possible to do this using Predicate interface.是否可以使用 Predicate 接口来做到这一点。

I have a client class that utilizes functions provided by a MathUtility class.我有一个使用 MathUtility 类提供的函数的客户端类。 Whatever the Mathmatical operation it should happen only within the MathUtility class.无论是什么数学运算,它都应该只在 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
    }

Right now it fails with this error - The method reduce(T, BinaryOperator<T>) in the type Stream<T> is not applicable for the arguments (int, T::sum)现在它因此错误而失败 - 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 Number types注意:我不想为不同的 Number 类型编写 sum 函数

EDIT: Detailed discussion on this topic covered in this Github Notebook编辑:此Github Notebook 中涵盖的有关此主题的详细讨论

Is there a way to do it without writing a sum function for every possible type of T that i'm expecting?有没有办法在不为我期望的每种可能类型的T编写 sum 函数的情况下做到这一点?

As Aaron Davis stated in a comment above, you can pass the reduction parameters to the method itself.正如 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);
}

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
  1. you must point out which actual type of Number to be summed, Since the Number class has no static sum method.您必须指出要求和的实际Number类型,因为Number类没有静态 sum 方法。
  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 .您必须为T extends Number类型分配identityT extends Number0是 Integer 的具体类型,与T类型不兼容。

Possible Solution可能的解决方案

you can make which actual type of Number to be summed later, for example:您可以稍后对哪种实际类型的Number求和,例如:

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:或者,您可以预先对哪种实际类型的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);
    }
}

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.需要注意的一点是,加法逻辑不会发生在调用端,而是只发生在 MathUtility 中。 The downside here is that you have to create Addition classes for every Number type you want the + operation.这里的缺点是您必须为每个您想要 + 操作的 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);
    }

}

The answer is yes, that should be possible.答案是肯定的,这应该是可能的。 The you defined is not known to have the method "sum", therefore the compiler complains.不知道您定义的具有“sum”方法,因此编译器会抱怨。 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) (我没有在 IDE 中尝试过这段代码,但这应该可以解决问题)

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

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