Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous overloaded java methods with generics and varargs

I'm trying to understand how java deals with ambiguities in function calls. In the following code, the call to method is ambiguous, but method2 is not!!!.

I feel both are ambiguous, but why does this compile when I comment out the call to method? Why is method2 not ambiguous as well?

public class A {     public static <K> List<K> method(final K arg, final Object... otherArgs) {         System.out.println("I'm in one");         return new ArrayList<K>();     }      public static <K> List<K> method(final Object... otherArgs) {         System.out.println("I'm in two");         return new ArrayList<K>();     }      public static <K, V> Map<K, V> method2(final K k0, final V v0, final Object... keysAndValues) {         System.out.println("I'm in one");         return new HashMap<K,V> ();     }      public static <K, V> Map<K, V> method2(final Object... keysAndValues) {         System.out.println("I'm in two");         return new HashMap<K,V>();     }      public static void main(String[] args) {         Map<String, Integer> c = A.method2( "ACD", new Integer(4), "DFAD" );         //List<Integer> d = A.method(1, "2", 3  );     } } 

EDIT: This came up in comments: A number of IDEs report both as ambiguous - IntelliJ and Netbeans so far. However, it compiles just fine from command-line/maven.

like image 991
Chip Avatar asked May 17 '12 19:05

Chip


People also ask

How do the overloading methods can be ambiguous?

There are ambiguities while using variable arguments in Java. This happens because two methods can definitely be valid enough to be called by data values. Due to this, the compiler doesn't have the knowledge as to which method to call.

What is varargs method in Java?

Varargs is a short name for variable arguments. In Java, an argument of a method can accept arbitrary number of values. This argument that can accept variable number of values is called varargs. The syntax for implementing varargs is as follows: accessModifier methodName(datatype… arg) { // method body }

How many varargs can we have in a function?

There can be only one variable argument in a method. Variable argument (Varargs) must be the last argument.


1 Answers

An intuitive way to test whether method1 is more specific than method2 is to see whether method1 can be implemented by invoking method2 with the same parameters

method1(params1){     method2(params1);   // if compiles, method1 is more specific than method2 } 

If there are varargs, we may need to expand a vararg so that 2 methods have same number of params.

Let's check the first two method()s in your example

<K> void method_a(K arg, Object... otherArgs) {     method_b(arg, otherArgs);   //ok L1 } <K> void method_b(Object arg, Object... otherArgs) { // extract 1 arg from vararg     method_a(arg, otherArgs);   //ok L2 } 

(return types are not used in determining specificity, so they are omitted)

Both compile, therefore each is more specific than the other, hence the ambiguity. Same goes for your method2()s, they are more specific than each other. Therefore the call to method2() is ambiguous and shouldn't compile; otherwise it's a compiler bug.


So that's what the spec says; but is it proper? Certainly, method_a looks more specific than method_b. Actually if we have a concrete type instead of K

void method_a(Integer arg, Object... otherArgs) {     method_b(arg, otherArgs);   // ok } void method_b(Object arg, Object... otherArgs) {     method_a(arg, otherArgs);   // error } 

then only method_a is more specific than method_b, not vice versa.

The discrepancy arises from the magic of type inference. L1/L2 calls a generic method without explicit type arguments, so the compiler tries to infer the type arguments. The goal of type inference algorithm is to find type arguments such that the code compiles! No wonder L1 and L2 compile. L2 is actually infer to be this.<Object>method_a(arg, otherArgs)

Type inference tries to guess what the programmer wants, but the guess must be wrong sometimes. Our real intention is actually

<K> void method_a(K arg, Object... otherArgs) {     this.<K>method_b(arg, otherArgs);   // ok } <K> void method_b(Object arg, Object... otherArgs) {     this.<K>method_a(arg, otherArgs);   // error } 
like image 70
irreputable Avatar answered Sep 21 '22 22:09

irreputable