Compilation error for an ambiguous method reference when using a typed collection
In the same class, I have two overloading methods:
public static <T> void foo(Collection<T> collection, T valueToAppend);
public static <T> void foo(Collection<T> collection, Collection<T> valueToAppend);
The next test should call the second method:
@Test
public void testFoo() {
ArrayList ftList = Lists.newArrayList();
List<Double> doubleList = Lists.newArrayList(1.0, 2.0);
foo(ftList, doubleList);
}
When I run the test, I get the following compilation error:
the reference to foo is ambiguous, and the foo (java.util.Collection, T) method in path.to.class and the foo (java.util.Collection, java.util.Collection) method in match.to.class match.
I am passing the collection in the second argument, so why doesn't the compiler know to go to the second method?
If I change method signatures and remove generics from the first parameter, I will not get a compilation error, why is that?
source to share
Here is my explanation. When the compiler has to choose between overloads, it always chooses the most specific overload. When you strip the type information from the first parameter, the captions become
public static <T> void foo(Collection collection, T valueToAppend)
public static <T> void foo(Collection collection, Collection<T> valueToAppend)
The second one is more specific. Any pair of parameters that can be accepted by the second method can also be accepted by the first, since either Collection
is Object
. So when you remove the type parameter, there is no ambiguity - the second method is chosen if you pass two Collection
s.
However, with type information, the signatures look like this:
public static <T> void foo(Collection<T> collection, T valueToAppend)
public static <T> void foo(Collection<T> collection, Collection<T> valueToAppend)
None of these signatures are more specific than the other. Parameters new ArrayList<String>()
and "Foo"
will be accepted by the first signature, but not the second. The parameters new ArrayList<String>()
and new ArrayList<String>()
will be accepted by the second signature, but not by the first.
So, if both signatures apply, then a problem arises.
You are trying to pass raw ArrayList
and List<Double>
. Since you are using a raw type ArrayList
(which you should never do) ftList
can pass as Collection<T>
for anyone T
(Try it. Try passing a raw List
method with a type parameter List<String>
. It works). Therefore you only need to see what the doubleList
second parameter matches for both overloads. It matches the first signature if T
- List<Double>
and matches the second signature if T
- Double
.
source to share