Per this question, Java will select the "most specific" option when trying to select between ambiguous overloaded constructors. In this example:
public class Test{
private Test(Map map){
System.out.println("Map");
}
private Test(Object o){
System.out.println("Object");
}
public static void main(String[] args){
new Test(null);
}
}
it will print
"Map"
However, I was trying to figure out exactly what "most specific" means. I assumed it meant "least ambiguous", as in "may refer to the fewest possible types." In this context, Object
may be anything that isn't a primitive, while Map
may only be Map
or ? extends Map
. Basically, I assumed that whichever class was closer to the leaf of the inheritance tree would be selected. That works when one class is a subclass of the other:
public class Test{
private Test(A a){
System.out.println("A");
}
private Test(B b){
System.out.println("B");
}
public static void main(String[] args){
new Test(null);
}
}
class A{}
class B extends A{}
"B"
Then I came up with this:
public class Test{
private Test(A a){
System.out.println("A");
}
private Test(E e){
System.out.println("E");
}
public static void main(String[] args){
new Test(null);
}
}
class A{}
class B extends A{}
class C{}
class D extends C{}
class E extends D{}
I would think it should print E
, as E
may only refer to one known type, whereas A
may refer to two (A
and B
). But it give an ambiguous reference error.
How is it actually choosing the constructor? I read through the docs but frankly I couldn't quite follow how it determines specificity. I'm hoping for a description of exactly why it can't determine that E
is more specific than A
.
It's not based on the number of types that are convertible to the parameter type - it's whether any value that's valid for one overload is valid for another, due to implicit conversions.
For example, there's an implicit conversion from String
to Object
, but the reverse isn't true, so String
is more specific than Object
.
Likewise there's an implicit conversion from B
to A
, but the reverse isn't true, so B
is more specific than A
.
With A
and E
however, neither is more specific than the other - there no conversion from A
to E
, and no conversion from E
to A
. That's why overload resolution fails.
The relevant bit of the JLS is actually 15.12.2.5, which includes this that might make it easier for you to understand:
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error.
So if you have:
void foo(String x)
void foo(Object x)
every invocation handled by foo(String)
could be handled by foo(Object)
, but the reverse is not the case. (For example, you could call foo(new Object())
and that couldn't be handled by foo(String)
.)
See more on this question at Stackoverflow