Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 481
ewok Avatar asked Aug 16 '16 16:08

ewok


People also ask

Can constructor call another overloaded constructor?

We can call an overloaded constructor from another constructor using this keyword but the constructor must be belong to the same class, because this keyword is pointing the members of same class in which this is used. This type of calling the overloaded constructor also termed as Constructor Chaining.

How can parameterized constructors be overloaded?

Using this() in constructor overloading this() reference can be used during constructor overloading to call default constructor implicitly from parameterized constructor. Please note, this() should be the first statement inside a constructor.

Can we overload and override constructor in Java?

Constructor looks like method but it is not. It does not have a return type and its name is same as the class name. But, a constructor cannot be overridden. If you try to write a super class's constructor in the sub class compiler treats it as a method and expects a return type and generates a compile time error.

Can you have an overloaded constructor in Java?

Yes! Java supports constructor overloading. In constructor loading, we create multiple constructors with the same name but with different parameters types or with different no of parameters.


2 Answers

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

like image 198
Jon Skeet Avatar answered Sep 20 '22 20:09

Jon Skeet


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.
like image 39
akash Avatar answered Sep 21 '22 20:09

akash