执行映射缩减操作的通用方法。(Java-8)
在Java8中如何使用泛型参数重载函数执行映射缩减操作的通用方法。(Java-8),java,function,generics,java-8,overloading,Java,Function,Generics,Java 8,Overloading,在Java8中如何使用泛型参数重载函数 public class Test<T> { List<T> list = new ArrayList<>(); public int sum(Function<T, Integer> function) { return list.stream().map(function).reduce(Integer::sum).get(); } public d
public class Test<T> {
List<T> list = new ArrayList<>();
public int sum(Function<T, Integer> function) {
return list.stream().map(function).reduce(Integer::sum).get();
}
public double sum(Function<T, Double> function) {
return list.stream().map(function).reduce(Double::sum).get();
}
}
公共类测试{
列表=新的ArrayList();
公共整数和(函数){
return list.stream().map(function).reduce(Integer::sum).get();
}
公共双和(函数){
return list.stream().map(function).reduce(Double::sum).get();
}
}
错误:java:名称冲突:
sum(java.util.function.function)和
sum(java.util.function.function)具有相同的擦除
您需要做的是定义扩展参数类型的自定义函数接口:
public class Test<T> {
List<T> list = new ArrayList<>();
@FunctionalInterface
public interface ToIntFunction extends Function<T, Integer>{}
public int sum(ToIntegerFunction function) {
return list.stream().map(function).reduce(Integer::sum).get();
}
@FunctionalInterface
public interface ToDoubleFunction extends Function<T, Double>{}
public double sum(ToDoubleFunction function) {
return list.stream().map(function).reduce(Double::sum).get();
}
}
公共类测试{
列表=新的ArrayList();
@功能接口
函数的公共接口扩展函数{}
公共整数和(ToIntegerFunction){
return list.stream().map(function).reduce(Integer::sum).get();
}
@功能接口
双函数的公共接口扩展函数{}
公共双和(ToDoubleFunction){
return list.stream().map(function).reduce(Double::sum).get();
}
}
另一种方法是使用java.util.function.ToIntFunction和java.util.function.ToDoubleFunction:
public class Test<T> {
List<T> list = new ArrayList<>();
@FunctionalInterface
public int sum(ToIntFunction function) {
return list.stream().mapToInt(function).sum();
}
public double sum(ToDoubleFunction function) {
return list.stream().mapToDouble(function).sum();
}
}
公共类测试{
列表=新的ArrayList();
@功能接口
公共整数和(ToIntFunction函数){
返回list.stream().mapToInt(函数).sum();
}
公共双和(ToDoubleFunction){
返回list.stream().mapToDouble(函数).sum();
}
}
您在问题中给出的示例与Java 8无关,而与泛型在Java中的工作方式有关<代码>功能功能和功能
将在编译时执行,并将转换为功能
。方法重载的经验法则是具有不同数量、类型或顺序的参数。由于您的两个方法都将转换为接受一个函数
参数,因此编译器对此表示不满
尽管如此,它已经提供了一种解决问题的方法。该解决方案的问题在于,对于不同类型(整数、双精度等)上的每种类型的操作(加法、减法等),您必须不断修改Test
类。另一种解决方案是使用方法重写
而不是方法重载
:
将测试
等级稍微更改如下:
public abstract class Test<I,O extends Number> {
List<I> list = new ArrayList<>();
public O performOperation(Function<I,O> function) {
return list.stream().map(function).reduce((a,b)->operation(a,b)).get();
}
public void add(I i) {
list.add(i);
}
public abstract O operation(O a,O b);
}
public static void main(String []args) {
Test<String,Integer> test = new MapStringToIntAddtionOperation();
test.add("1");
test.add("2");
System.out.println(test.performOperation(Integer::parseInt));
}
然后,客户端代码可以使用上述代码,如下所示:
public abstract class Test<I,O extends Number> {
List<I> list = new ArrayList<>();
public O performOperation(Function<I,O> function) {
return list.stream().map(function).reduce((a,b)->operation(a,b)).get();
}
public void add(I i) {
list.add(i);
}
public abstract O operation(O a,O b);
}
public static void main(String []args) {
Test<String,Integer> test = new MapStringToIntAddtionOperation();
test.add("1");
test.add("2");
System.out.println(test.performOperation(Integer::parseInt));
}
publicstaticvoidmain(字符串[]args){
Test Test=新的MapStringToIntAdditionOperation();
测试。添加(“1”);
测试。添加(“2”);
System.out.println(test.performOperation(Integer::parseInt));
}
使用这种方法的优点是,您的测试
类符合打开-关闭
原则。要添加一个新的运算,如乘法,只需添加一个新的子类Test
和override
两个数字相乘的运算
方法。将这与模式结合起来,您甚至可以最小化必须创建的子类的数量
注意此答案中的示例仅供参考。有许多方面需要改进(例如使
测试成为一个功能接口而不是抽象类),这超出了问题的范围。@srborlongan的解决方案不会很好地工作:)
请参见一个类似的示例-方法-comparingDouble(ToDoubleFunction)
,comparingit(ToIntFunction)
,等等。这些方法有不同的名称,因为这里重载不是一个好主意
原因是,当您执行sum(t->{…})
时,编译器无法推断要调用哪个方法;实际上,在推断隐式lambda表达式的类型(基于该方法的签名)之前,它需要首先解决方法重载问题,选择一个方法
这令人失望。在早期阶段,Java8有一个更复杂的推理机,Comparator
重载了comparing()
方法;和sum(t->{…})
也可以正确推断。不幸的是,他们决定简单地解决这个问题
重载带有函数参数的方法的经验法则:函数接口的算术必须不同,除非两者都是0
// OK, different arity
m1( X->Y )
m1( (X1, X2)->Y )
// not OK, both are arity 1
m2( X->Y )
m2( A->B )
m2( t->{...} ); // fail; type of `t` cannot be inferred
// OK! both are arity 0
m3( ()->Y )
m3( ()->B )
使用arity 0重载可以的原因是lambda表达式不是隐式的-所有参数类型都是已知的(因为没有参数!),我们不需要上下文信息来推断lambda类型
m3( ()-> return new Y() ); // lambda type is ()->Y
m3( ()-> return new B() ); // lambda type is ()->B
与Java 7、6和5相同:你不能。这是消息告诉你的。你建议使用包装器接口是好的,但不是“干净的”,因为每次需要引入新操作或新类型时,Test
都需要修改。只需两种类型(整数或双倍)和两种操作(求和和和和和乘法),您需要4个方法。更不用说为每种类型创建的包装器接口了。这是API的一个大爆炸。不过,对于链接来说,这并不很好:)请参阅类似的示例comparingDouble(ToDoubleFunction)
,comparingit(ToIntFunction)
-这些方法有不同的名称,因为重载不是一个好主意。只使用不同的方法名称要容易得多:)sumInt,sumDouble
,通过在Test
中设置操作
a受保护的
方法就可以很容易地安排这些方法,在子类中创建一个sumXYZ
方法,并在其中调用重写的操作
方法:但正如我所说,有很多方面的改进超出了答案的范围。。