将Java 8 Lambdas与generics一起使用
是否可以使用Predicate接口执行此操作。
我有一个客户端类,它使用MathUtility类提供的函数。 无论Mathmatical操作是什么,它都应该只在MathUtility类中发生。
//in client MathUtility.sum(listOfInts, (Integer i)->{return (i<3);}); //in utility class MathUtility { public static T sumWithCondition(List numbers, Predicate condition) { return numbers.parallelStream() .filter(condition) .map(i -> i) .reduce(0, T::sum); //compile time error } public static T avgWithCondition(List numbers, Predicate condition) { //another function } //lot many functions go here }
现在它失败并出现此错误 – The method reduce(T, BinaryOperator) in the type Stream is not applicable for the arguments (int, T::sum)
注意:我不想为不同的Number类型编写sum函数
有没有办法在没有为我期待的每种可能类型的
T
编写求和函数的情况下做到这一点?
正如Aaron Davis在上面的评论中所说,您可以将减少参数传递给方法本身。
public static T sumWithCondition(List numbers, Predicate condition, T identity, BinaryOperator accumulator) { return numbers.parallelStream().filter(condition).reduce(identity, accumulator); }
一个例子是:
List list = Arrays.asList(1, 2, 3, 4, 5); System.out.println(sumWithCondition(list, i -> i > 1, 0, (a, b) -> a + b)); >> 14 List list2 = Arrays.asList(BigInteger.ONE, BigInteger.ONE); System.out.println(sumWithCondition(list2, i -> true, BigInteger.ZERO, (a, b) -> a.add(b))); >> 2
- 你必须指出要求和的实际数字类型,因为
Number
类没有静态求和方法。 - 你必须分配类型为
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 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 Sum sum(List numbers, Predicate condition) { return sum(numbers.parallelStream().filter(condition)); } private static Sum sum(Stream stream) { return new Sum() { public T as(Class type) { return SumOp.of(type).sum(stream); } }; } interface Sum { T as(Class type); } }
SumOp
public class SumOp { private static final Map, SumOp>> OPERATORS = new HashMap<>(); private final T identity; private final BinaryOperator plusOp; private final Function 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 void register(Class type, SumOp sumOp) { OPERATORS.put(type, sumOp); } public static SumOp of(Class type) { return (SumOp ) OPERATORS.computeIfAbsent(type, it -> { String message = "No SumOp registered for type:" + type.getName(); throw new IllegalArgumentException(message); }); } public SumOp(T identity, BinaryOperator plusOp, Function valueExtractor) { this.identity = identity; this.valueExtractor = valueExtractor; this.plusOp = plusOp; } public T sum(List numbers, Predicate 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 { static class IntegerAddition implements BinaryOperator { @Override public Integer apply(Integer t, Integer u) { return t + u; } } public static Optional sum(List list, Predicate condition, BinaryOperator 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中尝试过这个代码,但是应该这样做)