ewok
ewok

Reputation: 21483

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.

Upvotes: 35

Views: 1371

Answers (3)

Akash Thakare
Akash Thakare

Reputation: 22982

Following statement of the JSL§15.12.2.5 answers that,

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.

Case 1

  • You can pass anything in constructor which takes Object while we can not pass anything other than a Map in first constructor. So whatever I pass in Map constructor can be handled by Object constructor and that's why Test(Map map) becomes mote specific.

Case 2

  • Since B extends A, here Test(B b) constructor becomes more specific. As we can pass B in Test(A a) thanks to inheritance.

Case 3

  • In this case there is no direct conversion to depict the more specific method and it results in ambiguity.

Upvotes: 9

Jon Skeet
Jon Skeet

Reputation: 1501626

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

Upvotes: 45

Jordi
Jordi

Reputation: 2537

This behavior is because E is not more specific than A, as they belong to different hierarchies they cannot be compared. Thus, when you pass a null, Java cannot know which hierarchy to use.

Upvotes: 6

Related Questions