简体   繁体   中英

Ambiguous overloaded generic method with wildcard in parameter

Given the following declarations

interface Base<A> { }

interface Special<A,B> extends Base<A> { }

<T> void foo(Base<T> b) {}

<T> void foo(Special<?,T> s) {}

Why do I get a compile error for the following code:

Special<String, Integer> s = null;
foo(s); // error: reference to foo is ambiguous

BTW, the problem can be fixed by changing the declaration of the second method to

<T,X> void foo(Special<X,T> s) {}

First of all, a very interesting question.

Without generics

Consider the following code:

interface NoGenericsBase { }

interface NoGenericsSpecial extends NoGenericsBase { }

interface NoGenericsSuperSpecial extends NoGenericsSpecial { }

void foo(NoGenericsBase b) {
    System.out.println("Feel tha base");
}

void foo(NoGenericsSpecial s) {
    System.out.println("Special delivery");
}

We've always been taught that the compiler picks the most specific method . And indeed, the abovementioned code will, provided you call foo((NoGenericsSuperSpecial) null) , print the following:

Special delivery

So far, so good.

Generics

Now, let's test some generics behaviour:

interface Base<T> { }

interface Special<T> extends Base<T> { }

void foo(Base<? extends Number> b) {
    System.out.println("Feel tha base");
}

void foo(Special<? extends Number> s) {
    System.out.println("Special delivery");
}

public static void main(String[] args) {
    Special<Integer> v = null;
    new Main().foo(v);
}

This code compiles. The compiler finds two matches – both Base<? extends Number> Base<? extends Number> as Special<? extends Number> Special<? extends Number> apply –, but the compiler is able to figure out which is the most specific: it will pick void foo(Special<? extends Number>) , because both captures of the unbound wildcards are equal.

But let us rewrite foo(Base<...>) method and leave the remaining untouched:

void foo(Base<? extends Integer> b) {
    System.out.println("Feel tha base");
}

Now the following error occurs:

reference to foo is ambiguous
both method foo(Base<? extends Integer>) and method foo(Special<? extends Number>) match

Before figuring out the most specific type matching, the compiler handles type variables. Apparently, the compiler cannot figure out whether <? extends Number> <? extends Number> or <? extends Integer> <? extends Integer> applies, unregarded the type of the variable itself ( Base or Special ).

It seems that the variable type handling comes prior to the selection of method signatures regarding inheritance.

One (or at least I myself) should expect the compiler to pick foo(Special<? extends Number>) , but this is not the case.

The reason

I do not know whether the compiler is not able to pick the most specific one regarding generics, or it's not configured to do so.

See the Java Language Specification § 18.5 or § 4.5.1 for more details.

Let me take some time to read more of generics, so maybe we can figure it out...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM