A general method for performing a map reduction operation. (Java-8)

How to overload a function using a generic parameter in Java 8?

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(); } } 

Error: java: name clash: sum (java.util.function.Function <T, java.lang.Double>) and sum (java.util.function.Function <T, java.lang.Integer>) have the same erasure

+8
java function generics overloading java-8
source share
3 answers

The example you present in your question has nothing to do with Java 8 and it all depends on how generics work in Java. Function<T, Integer> function and Function<T, Double> function will go through type-erasure at compilation and will be converted to Function . A rule of thumb for method overloading is to have a different number, type, or sequence of parameters. Since both methods are converted to accept a Function argument, the compiler complains about this.

At the same time, srborlongan has already provided one way to solve the problem. The problem with this solution is that you must continue to modify your Test class for each type of operation (addition, subtraction, etc.) For different types (Integer, Double, etc.). An alternative solution would be to use method overriding instead of method overloading :

Modify the Test class as follows:

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

Create a subclass of Test that will add two Integer s.

 public class MapStringToIntAddtionOperation extends Test<String,Integer> { @Override public Integer operation(Integer a,Integer b) { return a+b; } } 

Client code can then use the code above as follows:

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

The advantage of using this approach is that your Test class is open-closed . To add a new operation, such as multiplication, all you have to do is add a new subclass of Test and an override the operation method to multiply the two numbers. Create this with the Decorator template, and you can even minimize the number of subclasses you need to create.

Note The example in this answer is indicative. There are many areas of improvement (for example, making Test functional interface instead of an abstract class) that go beyond the scope of the question.

+3
source share

Benji Weber once wrote about this to get around this. What you need to do is define custom functional interfaces that extend the types for your parameters:

 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(); } } 

Another way is to use java.util.function.ToIntFunction and java.util.function.ToDoubleFunction instead:

 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(); } } 
+6
source share

@Srborlongan's solution will not work very well :)

See a similar example - Comparator methods - comparingDouble(ToDoubleFunction) , comparingInt(ToIntFunction) , etc. Methods have different names, since overloading is not a good idea here.

The reason is that when you execute sum(t->{...}) , the compiler cannot determine which method to call; in fact, it needs to first enable the method overload, select one method before deriving the type of the implicit lambda expression (based on this method signature).

This is disappointing. At an early stage, Java8 had a more complex output mechanism, and Comparator overloaded the comparing() methods; and sum(t->{...}) will be correctly output. Unfortunately, they just decided this :( And here we are now.

Rule of thumb for overload methods with functional arguments: the arities of functional interfaces should be different if both of them are not equal to 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 ) 

The reason that overloading with arity 0 is ok is that the lambda expressions will not be implicit - all argument types are known (since there are no arguments!), We do not need contextual information to display the lambda type

 m3( ()-> return new Y() ); // lambda type is ()->Y m3( ()-> return new B() ); // lambda type is ()->B 
0
source share

All Articles