执行map-reduce操作的通用方法。 (爪哇-8)
如何在Java 8中使用generics参数重载函数?
public class Test { List list = new ArrayList(); public int sum(Function function) { return list.stream().map(function).reduce(Integer::sum).get(); } public double sum(Function function) { return list.stream().map(function).reduce(Double::sum).get(); } }
错误:java:name clash:sum(java.util.function.Function )和sum(java.util.function.Function )具有相同的擦除
您在问题中提出的示例与Java 8无关,而且与generics在Java中的工作方式有关。 Function
和Function
在编译时将经过类型擦除 ,并将转换为Function
。 方法重载的经验法则是具有不同的参数数量,类型或顺序。 由于您的两个方法都将转换为使用Function
参数,因此编译器会抱怨它。
话虽这么说, srborlongan已经提供了一种解决问题的方法。 该解决方案的问题在于您必须在不同类型(Integer,Double等)上为每种类型的操作(加法,减法等)继续修改Test
类。 另一种解决方案是使用method overriding
而不是method overloading
:
稍微更改Test
类,如下所示:
public abstract class Test { List list = new ArrayList<>(); public O performOperation(Function 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); }
创建一个Test
的子类,它将添加两个Integer
。
public class MapStringToIntAddtionOperation extends Test { @Override public Integer operation(Integer a,Integer b) { return a+b; } }
然后客户端代码可以使用上面的代码,如下所示:
public static void main(String []args) { Test test = new MapStringToIntAddtionOperation(); test.add("1"); test.add("2"); System.out.println(test.performOperation(Integer::parseInt)); }
使用此方法的优点是您的Test
类符合open-closed
原则。 要添加诸如乘法之类的新操作,您所要做的就是添加一个新的Test
子类并override
operation
方法以乘以两个数字。 使用Decorator模式对其进行分析,您甚至可以最大限度地减少必须创建的子类的数量。
注意此答案中的示例是指示性的。 有很多方面的改进(例如make Test
是一个function界面而不是抽象类)超出了问题的范围。
Benji Weber曾写过一种规避这种方法的方法。 您需要做的是定义扩展参数类型的自定义function接口:
public class Test { List list = new ArrayList<>(); @FunctionalInterface public interface ToIntFunction extends Function{} public int sum(ToIntegerFunction function) { return list.stream().map(function).reduce(Integer::sum).get(); } @FunctionalInterface public interface ToDoubleFunction extends Function{} public double sum(ToDoubleFunction function) { return list.stream().map(function).reduce(Double::sum).get(); } }
另一种方法是使用java.util.function.ToIntFunction和java.util.function.ToDoubleFunction:
public class Test { List 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(); } }
@srborlongan的解决方案不会很好用:)
看一个类似的例子 – 比较器方法 – comparingDouble(ToDoubleFunction)
, comparingInt(ToIntFunction)
等。方法有不同的名称,因为重载在这里不是一个好主意。
原因是,当你做sum(t->{...})
,编译器无法推断调用哪个方法; 实际上它需要首先解决方法重载,选择一个方法,然后推断隐式lambda表达式的类型(基于该方法的签名)
这令人失望。 在早期阶段,Java8有一个更复杂的推理引擎, Comparator
重载了compare comparing()
方法; 和sum(t->{...})
也将被正确推断。 不幸的是,他们决定简单地说:(现在我们在这里。
具有function参数的重载方法的经验法则:function接口的arities必须不同,除非两者都是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