ewok ewok - 3 months ago 10
Java Question

Java: selection between overloaded constructors

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
.

Answer

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).)

Comments