Case 1
static void call(Integer i) {
System.out.println("hi" + i);
}
static void call(int i) {
System.out.println("hello" + i);
}
public static void main(String... args) {
call(10);
}
Output of Case 1 : hello10
Case 2
static void call(Integer... i) {
System.out.println("hi" + i);
}
static void call(int... i) {
System.out.println("hello" + i);
}
public static void main(String... args) {
call(10);
}
Shows compilation error reference to call ambiguous. But, I was unable to understand. Why ? But, when I commented out any of the call() methods from Case 2, then It works fine. Can anyone help me to understand, what is happening here ?
Finding the most specific method is defined in a very formal way in the Java Language Specificaion (JLS). I have extracted below the main items that apply while trying to remove the formal formulae as much as possible.
In summary the main items that apply to your questions are:
JLS 15.12.2: your use case falls under phase 3:
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
Then JLS 15.12.2.4 basically determines that both method are applicable, because 10 can be converted to both an Integer... or an int.... So far so good. And the paragraph concludes:
The most specific method (§15.12.2.5) is chosen among the applicable variable-arity methods.
Which brings us to JLS 15.12.2.5. This paragraph gives the conditions under which an arity method m(a...) is more specific than another arity method m(b...). In your use case with one parameter and no generics, it boils down to:
m(a...) is more specific than m(b...) iif a <: b, where <: means is a subtype of.
It happens that int is not a subtype of Integer and Integer is not a subtype of int.
To use the JLS language, both call methods are therefore maximally specific (no method is more specific than the other). In this case, the same paragraph concludes:
If all the maximally specific methods have override-equivalent (§8.4.2) signatures [...] => not your case as no generics are involved and Integer and int are different parameters
Otherwise, we say that the method invocation is ambiguous, and a compile-time error occurs.
NOTE
If you replaced Integer... by long... for example, you would have int <: long and the most specific method would be call(int...)*.
Similarly, if you replaced int... by Number..., the call(Integer...) method would be the most specific.
*There was actually a bug in JDKs prior to Java 7 that would show an ambiguous call in that situation.
Looks like it's related to bug #6886431, which seems to be fixed in OpenJDK 7.
Below is the bug description,
Bug Description:
When invoking a method with the following overloaded signatures, I
expect an ambiguity error (assuming the arguments are compatible with
both):
int f(Object... args);
int f(int... args);
javac treats the second as more specific than the first. This
behavior is sensible (I prefer it), but is inconsistent with the JLS
(15.12.2).
from JLS 15.12.2.2
JLS 15.12.2.2 Choose the Most Specific Method
IIf more than one method declaration is both accessible and applicable
to a method invocation, it is necessary to choose one to provide the
descriptor for the run-time method dispatch. The Java programming
language uses the rule that the most specific method is chosen. The
informal intuition is that one method declaration 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 type error.
neither of these methods can be passed to the other (the types for int[] and Integer[] arent related) hence the call is ambiguous
The compiler doesn't know which method should be called. In order to fix this, you need to cast the input parameters..
public static void main(String... args) {
call((int)10);
call(new Integer(10));
}
EDIT:
It is because the compiler tries to convert the Integer into int, Therefore, an implicit cast takes place prior to invocation of the call method. So the compiler then looks for any methods by that name that can take ints. And you have 2 of them, so the compiler doesn't know which of both should be called.
If more than one method can be applicable, than from the Java Language Specification we Choosing the Most Specific Method, paragraph 15.12.2.5:
One variable arity member method named m is more specific than another variable arity member method of the same name if either (<: means subtyping):
One member method has n parameters and the other has k parameters, where n ≥ k, and:
The types of the parameters of the first member method are T1, ..., Tn-1, Tn[].
(we have only one T_n[], which is Integer[], n=1)
The types of the parameters of the other method are U1, ..., Uk-1, Uk[]. (again only one paramenter, which is int[], k=1)
If the second method is generic then let R1 ... Rp (p ≥ 1) be its type parameters, let Bl be the declared bound of Rl (1 ≤ l ≤ p), let A1 ... Ap be the type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ti << Ui (1 ≤ i ≤ k-1) and Ti << Uk (k ≤ i ≤ n), and let Si = Ui[R1=A1,...,Rp=Ap] (1 ≤ i ≤ k). (method is not generic)
Otherwise, let Si = Ui (1 ≤ i ≤ k). (S1 = int[])
For all j from 1 to k-1, Tj <: Sj, and, (nothing here)
For all j from k to n, Tj <: Sk, and, (Compare T1 <: S1, Integer[] <: int[])
If the second method is a generic method as described above, then Al <: Bl[R1=A1,...,Rp=Ap] (1 ≤ l ≤ p). (method is not generic)
Although primitive int is autoboxed to wrapper Integer, int[] is not autoboxed to Integer[], than the first condition doesn't hold.
Second condition is almost the same.
There are also other conditions that do not hold, and then due to JLS:
we say that the method invocation is ambiguous, and a compile-time error occurs.
This question has already been asked a number of times. The tricky part is that f(1, 2, 3) is clearly passing int's, so why can't the compiler pick the f(int...) version? The answer must lie somewhere in the JLS, which I'm scratching my heads against
According to §15.12.2.4, both methods are applicable variable-arity method, so the next step is identifying the most specific one.
Unofortunately, §15.12.2.5 uses the subtype test Ti <: Si between f1(T1, .. Tn) and f2(S1, .. Sn) formal parameters to identify the target method, and since there is no subtype relationship between Integer and int, no one wins, because neither int :> Integer nor Integer :> int. At the end of the paragraph is stated:
The above conditions are the only circumstances under which one method
may be more specific than another. [...]
A method m1 is strictly more specific than another method m2 if
and only if m1 is more specific than m2 and m2 is not more specific
than m1.
A method is said to be maximally specific for a method invocation
if it is accessible and applicable and there is no other method that
is applicable and accessible that is strictly more specific.
It is possible that no method is the most specific, because there are
two or more methods that are maximally specific. In this case:
[...]
Otherwise, we say that the method invocation is ambiguous, and a compile-time error occurs.
Attached a blog post by Gilad Bracha (see exhibit 2), in turn linked in the bug report from the #Jayamhona's answer.
Related
Consider the following article from the JLS (§15.13.1)
A method reference expression ending with Identifier is exact if it satisfies all of the following:
If the method reference expression has the form ReferenceType ::[TypeArguments] Identifier, then ReferenceType does not denote a raw type.
The type to search has exactly one member method with the name Identifier that is accessible to the class or interface in which the method reference expression appears.
This method is not variable arity (§8.4.1).
If this method is generic (§8.4.4), then the method reference expression provides
TypeArguments.
Consider the following code snippet:
class Scratch {
public static void main(String[] args) {
Scratch.funct(new ImplementingClass()::<Functional1>hitIt);
}
public static void funct(Functional1 a){}
public static void funct(Functional2 a){}
}
interface Functional1 {<T> T hitIt();}
interface Functional2 {<T> T hitIt();}
class ImplementingClass{
public <T> T hitIt(){return null;}
}
Clearly - this satisfies all the conditions being mentioned for a method reference to be exact.
Not sure why still the method reference is in-exact in this particular case? Am I missing something here from the clause?
Solution :
Based on inputs from #Sweeper #DidierL and #Holger here what I summarized:
Both the functional interfaces have the functionType <T> () -> T
the method reference …::<Functional1>hitIt substitutes T with Functional1, so the resulting functional signature is () -> Functional1 which does not match <T> () -> T.
First a warning: IANAJL (IANAL for Java 😉)
As far as I can tell, this should compile if you make the two interface methods non-generic, but it doesn’t. Let’s simplify the code as much as we can to reproduce the problem:
class Scratch {
public static void main(String[] args) {
Scratch.funct(ImplementingClass::<Void>hitIt);
}
public static void funct(Functional1 a){}
public static void funct(Functional2 a){}
}
interface Functional1 {Integer hitIt();}
interface Functional2 {String hitIt();}
class ImplementingClass{
public static <T> Integer hitIt(){return null;}
}
The simplifications:
the two interfaces now have non-generic methods
ImplementingClass.hitIt() is now static and has a concrete return type (non-generic)
Now let’s analyze the call to check if it should compile. I put links to the Java 8 specs but they are very similar in 17.
15.12.2.1. Identify Potentially Applicable Methods
A member method is potentially applicable to a method invocation if and only if all of the following are true:
[…]
If the member is a fixed arity method with arity n, the arity of the method invocation is equal to n, and for all i (1 ≤ i ≤ n), the i'th argument of the method invocation is potentially compatible, as defined below, with the type of the i'th parameter of the method.
[…]
An expression is potentially compatible with a target type according to the following rules:
[…]
A method reference expression (§15.13) is potentially compatible with a functional interface type if, where the type's function type arity is n, there exists at least one potentially applicable method for the method reference expression with arity n (§15.13.1), and one of the following is true:
The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is i) static and supports arity n, or ii) not static and supports arity n-1.
The method reference expression has some other form and at least one potentially applicable method is not static.
(this last bullet applies for the case of the question where the method reference uses a constructor invocation expression, i.e. a Primary)
At this point, we only check for the arity of the method reference, so both funct() methods are potentially applicable.
15.12.2.2. Phase 1: Identify Matching Arity Methods Applicable by Strict Invocation
An argument expression is considered pertinent to applicability for a potentially applicable method m unless it has one of the following forms:
[…]
An inexact method reference expression (§15.13.1).
[…]
This is the only bullet point in this list that could potentially match, however, as pointed in the question we have an exact method reference expression here. Note that if you remove the <Void>, this makes it an inexact method reference, and both methods should be applicable as per the next section:
Let m be a potentially applicable method (§15.12.2.1) with arity n and formal parameter types F1 ... Fn, and let e1, ..., en be the actual argument expressions of the method invocation. Then:
[…]
If m is not a generic method, then m is applicable by strict invocation if, for 1 ≤ i ≤ n, either ei is compatible in a strict invocation context with Fi or ei is not pertinent to applicability.
However only the first funct() method declaration should be applicable by strict invocation. Strict invocation contexts are defined here, but basically they check if the type of the expression matches the type of the argument. Here the type of our argument, the method reference, is defined by section 15.13.2. Type of a Method Reference whose relevant part is:
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of […] T.
[…]
A method reference expression is congruent with a function type if both of the following are true:
The function type identifies a single compile-time declaration corresponding to the reference.
One of the following is true:
The result of the function type is void.
The result of the function type is R, and the result of applying capture conversion (§5.1.10) to the return type of the invocation type (§15.12.2.6) of the chosen compile-time declaration is R' (where R is the target type that may be used to infer R'), and neither R nor R' is void, and R' is compatible with R in an assignment context.
Here R would be Integer for Functional1 and String for Functional2, while R' is Integer in both cases (since there is no capture conversion needed for ImplementingClass.hitIt()), so clearly the method reference is not congruent with Functional2 and by extension not compatible.
funct(Functional2) should thus not be considered for applicability by strict invocation, and since only funct(Functional1) remains it should be selected.
It should be noted that Javac must select both methods in Phase 1, because only one phase can apply, and Phase 2 only uses loose context instead of strict, which just allows boxing operations, and Phase 3 then includes varargs, which is not applicable either.
Except if we consider that Javac somehow considers the method reference as congruent with Functional2, the only reason I see for selecting both methods is if it considered the method reference as not pertinent for applicability as specified above, which I can only explain if the compiler considers it as an inexact method reference.
15.12.2.5. Choosing the Most Specific Method
This is where the compilation fails. We should note that there is nothing here that would make the compiler select one method over the other. The applicable rule is:
m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).
[…]
A type S is more specific than a type T for any expression if S <: T (§4.10).
This appears to work properly: change Functional2 to extend Functional1 and it will compile.
A functional interface type S is more specific than a functional interface type T for an expression e if T is not a subtype of S and one of the following is true (where U1 ... Uk and R1 are the parameter types and return type of the function type of the capture of S, and V1 ... Vk and R2 are the parameter types and return type of the function type of T):
If e is an explicitly typed lambda expression […]
If e is an exact method reference expression (§15.13.1), then i) for all i (1 ≤ i ≤ k), Ui is the same as Vi, and ii) one of the following is true:
R2 is void.
R1 <: R2.
[…]
This does not allow to disambiguate it either. However, changing Functional2.hitIt() to return Number should make Functional1 more specific since Integer <: Number.
This still fails, which seems to confirm that the compiler does not consider it as an exact method reference.
Note that removing the <T> in ImplementingClass.hitIt() allows it to compile, independently of the return type of Functional2.hitIt(). Fun fact: you can leave the <Void> at the call site, the compiler ignores it.
Even stranger: if you leave the <T> and add more type arguments than required at the call site, the compiler still complains about the ambiguous call and not about the number of type arguments (until you remove the ambiguity). Not that this should make the method reference inexact, based on the above definition, but I would think it should be checked first.
Conclusion
Since the Eclipse compiler accepts it, I would tend to consider this as a Javac bug, but note that the Eclipse compiler is sometimes more lenient than Javac with respect to the specs, and some similar bugs have been reported and closed (JDK-8057895, JDK-8170842, …).
Here's a code example that doesn't compile:
public class Test {
public static void main(String[] args) {
method(1);
}
public static void method(int... x) {
System.out.println("varargs");
}
public static void method(Integer... x) {
System.out.println("single");
}
}
Can someone tell me the reason why these methods are ambiguous ? Thank you in advance.
There are 3 phases used in overload resolution (JLS 15.2.2):
The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
In your example, both methods are variable arity methods, so the third phase applies.
Now, since we have two methods to choose from, we look for the more specific method.
JLS 15.12.2.5. Choosing the Most Specific Method says :
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
...
One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:
...
m2 is not generic, m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ k). Additionally, if m2 has k+1 parameters, then the k+1'th variable arity parameter type of m1 is a subtype of the k+1'th variable arity parameter type of m2.
In your case you have two non-generic methods which are applicable by variable arity invocation (i.e. both have varargs). In order for one of the methods to be chosen when you call method(1), one of them has to be more specific than the other. In your case, each method only has one parameter, and for one of them to be more specific than the other, the type of that one parameter must be a subtype of the other method's parameter.
Since int is not a sub-type of Integer and Integer is not a sub-type of int, neither of your methods is more specific than the other. Hence the The method method(int[]) is ambiguous for the type Test error.
An example that would work :
public static void method(Object... x) {
System.out.println("varargs");
}
public static void method(Integer... x) {
System.out.println("single");
}
Since Integer is a sub-type of Object, the second method would be chosen when you call method(1).
Consider the method signatures
public static void foo(int a)
and
public static void foo(Integer a)
Before boxing and unboxing, the call foo(1) would not have been ambiguous. To ensure compatibility with earlier versions of Java, the call remains unambiguous. Therefore the first phase of overload resolution does not allow for boxing, unboxing, or variable arity invocation, which were all introduced at the same time. Variable arity invocation is when you call a varargs method by passing a sequence of parameters for the last argument (rather than an array).
However the resolution of method(1) for your method signatures allows for boxing and unboxing because both methods require a variable arity invocation. Since boxing is allowed, both signatures apply. Normally when two overloadings apply, the most specific overloading is chosen. However neither of your signatures is more specific than the other (because neither int nor Integer is a subtype of the other). Therefore the call method(1) is ambiguous.
You can make this compile by passing new int[]{1} instead.
Because they are ambiguous. According to JLS you can either do widening, boxing or boxing-then-widening. In your example there are 2 methods parameters which can be boxed/unboxed to each other. On compile time though it's not visible because of varargs, which were always not absolutely clear in java.
Even Sun recommended developers not to overload varargs methods, there were bugs in compiler related to it (see here).
The difference between int and Integer is that Integer is an object type.you can use in situation like finding the maximum number of type int , or comparing to integers
Integer object is already associated with methods like compare method:
public static void method(int x, int y) {
System.out.println(Integer.compare(x, y));
}
Find more at : http://docs.oracle.com/javase/7/docs/api/
Here's a code example that doesn't compile:
public class Test {
public static void main(String[] args) {
method(1);
}
public static void method(int... x) {
System.out.println("varargs");
}
public static void method(Integer... x) {
System.out.println("single");
}
}
Can someone tell me the reason why these methods are ambiguous ? Thank you in advance.
There are 3 phases used in overload resolution (JLS 15.2.2):
The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
In your example, both methods are variable arity methods, so the third phase applies.
Now, since we have two methods to choose from, we look for the more specific method.
JLS 15.12.2.5. Choosing the Most Specific Method says :
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
...
One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:
...
m2 is not generic, m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ k). Additionally, if m2 has k+1 parameters, then the k+1'th variable arity parameter type of m1 is a subtype of the k+1'th variable arity parameter type of m2.
In your case you have two non-generic methods which are applicable by variable arity invocation (i.e. both have varargs). In order for one of the methods to be chosen when you call method(1), one of them has to be more specific than the other. In your case, each method only has one parameter, and for one of them to be more specific than the other, the type of that one parameter must be a subtype of the other method's parameter.
Since int is not a sub-type of Integer and Integer is not a sub-type of int, neither of your methods is more specific than the other. Hence the The method method(int[]) is ambiguous for the type Test error.
An example that would work :
public static void method(Object... x) {
System.out.println("varargs");
}
public static void method(Integer... x) {
System.out.println("single");
}
Since Integer is a sub-type of Object, the second method would be chosen when you call method(1).
Consider the method signatures
public static void foo(int a)
and
public static void foo(Integer a)
Before boxing and unboxing, the call foo(1) would not have been ambiguous. To ensure compatibility with earlier versions of Java, the call remains unambiguous. Therefore the first phase of overload resolution does not allow for boxing, unboxing, or variable arity invocation, which were all introduced at the same time. Variable arity invocation is when you call a varargs method by passing a sequence of parameters for the last argument (rather than an array).
However the resolution of method(1) for your method signatures allows for boxing and unboxing because both methods require a variable arity invocation. Since boxing is allowed, both signatures apply. Normally when two overloadings apply, the most specific overloading is chosen. However neither of your signatures is more specific than the other (because neither int nor Integer is a subtype of the other). Therefore the call method(1) is ambiguous.
You can make this compile by passing new int[]{1} instead.
Because they are ambiguous. According to JLS you can either do widening, boxing or boxing-then-widening. In your example there are 2 methods parameters which can be boxed/unboxed to each other. On compile time though it's not visible because of varargs, which were always not absolutely clear in java.
Even Sun recommended developers not to overload varargs methods, there were bugs in compiler related to it (see here).
The difference between int and Integer is that Integer is an object type.you can use in situation like finding the maximum number of type int , or comparing to integers
Integer object is already associated with methods like compare method:
public static void method(int x, int y) {
System.out.println(Integer.compare(x, y));
}
Find more at : http://docs.oracle.com/javase/7/docs/api/
Let's assume I have following code:
// Method acception generic parameter
public static <T> T foo(T para) {
return para;
}
// Method accepting Integer parameter
public static Integer foo(Integer para) {
return para + 1;
}
// Method accepting Number parameter
public static Number foo(Number para) {
return para.intValue() + 2;
}
public static void main(String[] args) {
Float f = new Float(1.0f);
Integer i = new Integer(1);
Number n = new Integer(1);
String s = "Test";
Number fooedFloat = foo(f); // Uses foo(Number para)
Number fooedInteger = foo(i); // Uses foo(Integer para)
Number fooedNumber = foo(n); // Uses foo(Number para)
String fooedString = foo(s); // Uses foo(T para)
System.out.println("foo(f): " + fooedFloat);
System.out.println("foo(i): " + fooedInteger);
System.out.println("foo(n): " + fooedNumber);
System.out.println("foo(s): " + fooedString);
}
The output looks the following:
foo(f): 3
foo(i): 2
foo(n): 3
foo(s): Test
Now the question(s):
foo(n) calls foo(Number para), most probably because n is defined as Number, even though it has an Integer assigned to it. So am I right in the assumption that the decision, which of the overloaded methods is taken happens at compile-time, without dynamic binding? (Question about static and dynamic binding)
foo(f) uses foo(Number para), while foo(i) uses foo(Integer para). Only foo(s) uses the generic version. So the compiler always looks if there is a non-generic implementation for the given types, and only if not it falls back to the generic version? (Question about generics)
Again, foo(f) uses foo(Number para), while foo(i) uses foo(Integer para). Yet, Integer i would also be a Number. So always the method with the "outer-most" type within the inheritance tree is taken? (Question about inheritance)
I know these are a lot questions, and the example is not taken from productive code, yet I just would like to know what "happens behind" and why things happen.
Also any links to the Java documentation or the Java specification are really appreciated, I could not find them myself.
The rules of determining which method signature to call at compile-time are explained in the language specification. Specifically important is the section on choosing the most specific method. Here are the parts related to your questions:
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
...
One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:
m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by §18.5.4.
m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).
...
A type S is more specific than a type T for any expression if S <: T (§4.10).
In this case, Integer is more specific than Number because Integer extends Number, so whenever the compiler detects a call to foo that takes a variable declared of type Integer, it will add an invocation for foo(Integer).
More about the first condition related to the second method being generic is explained in this section. It's a little verbose but I think the important part is:
When testing that one applicable method is more specific than another (§15.12.2.5), where the second method is generic, it is necessary to test whether some instantiation of the second method's type parameters can be inferred to make the first method more specific than the second.
...
Let m1 be the first method and m2 be the second method. Where m2 has type parameters P1, ..., Pp, let α1, ..., αp be inference variables, and let θ be the substitution [P1:=α1, ..., Pp:=αp].
...
The process to determine if m1 is more specific than m2 is as follows:
...
If Ti is a proper type, the result is true if Si is more specific than Ti for ei (§15.12.2.5), and false otherwise. (Note that Si is always a proper type.)
Which basically means that foo(Number) and foo(Integer) are both more specific than foo(T) because the compiler can infer at least one type for the generic method (e.g. Number itself) that makes foo(Number) and foo(Integer) more specific (this is because Integer <: Number and Number <: Number) .
This also means that in your code foo(T) is only applicable (and inherently the most specific method since it's only the one applicable) for the invocation that passes a String.
Am I right in the assumption that the decision, which of the overloaded methods is taken happens at compile-time, without dynamic binding?
Yes, Java chooses among available overloads of a method at compile time, based on the declared types of the arguments, from among the alternatives presented by the declared type of the target object.
Dynamic binding applies to choosing among methods having the same signature based on the runtime type of the invocation target. It has nothing directly to do with the runtime types of the actual arguments.
So the compiler always looks if there is a non-generic implementation for the given types, and only if not it falls back to the generic version?
Because of type erasure, the actual signature of your generic method is
Object foo(Object);
Of the argument types you tested, that is the best match among the overloaded options only for the String.
So always the method with the "outer-most" type within the inheritance tree is taken?
More or less. When selecting among overloads, the compiler chooses the alternative that best matches the declared argument types. For a single argument of reference type, this is the method whose argument type is the argument's declared type, or its nearest supertype.
Things can get dicey if Java has to choose among overloads of multiple-argument methods, and it doesn't have an exact match. When there are primitive arguments, it also has to consider the allowed argument conversions. The full details take up a largish section of the JLS.
So, it is pretty simple:
1) Yes, the decision is made at compile-time. The compiler chooses the method with the most specific matching type. So the compiler will choose the Number version when the variable you pass as an argument is declared as a Number, even if it is an Integer at run-time. (If the compiler finds two "equally matching" methods, an ambiguous method error will make the compilation fail)
2) At run-time, there are no generics, everything is just an Object. Generics are a compile-time and source-code feature only. Therefore the compiler must do the best he can, because the VM surely can not.
public void add(long... x){}
public void add(Integer... x){}
add(2);
this produces error...why overlaoding is not performed with both widening and boxing?
but overloading without vararg works fine
public void add(long x){}
public void add(Integer x){}
add(2);
here add(long x) will be executed that is widening beats boxing...why not same concept with
var arguments
Java compiler performs three attempts to choose an appropriate method overload (JLS §15.12.2.1):
Phase 1: Identify Matching Arity Methods Applicable by Subtyping
(possible boxing conversions and methods with varargs are ignored)
Phase 2: Identify Matching Arity Methods Applicable by Method
Invocation Conversion
(takes boxing conversion in account, but ignores methods with varargs)
Phase 3: Identify Applicable Variable Arity Methods
(examines all possibilities)
So, with your examples it works as follows:
Without varargs: add(long x) is identified as the only applicable method on the 1st phase (this method is applicable by subtyping since int is a subtype of long, §JLS 4.10.1), so that following phases are not executed.
With varargs: overload resoltion algorithm goes to phase 3, where both methods are identified as applicable, and compiler can't choose the most specific method of them (choosing the most specific method is yet another complex algorithm), therefore it reports ambiguity.
See also:
The Java Language Specification, Seventh Edition
because it is ambiguous.
2 can be Integer as well as long and it can be resolve to both. you made compiler confused whom to invoke :)
5.12.2.2 Choose the Most Specific Method
If more than one method declaration is
both accessible and applicable to a
method invocation, it is necessary to
choose one to provide the descriptor
for the run-time method dispatch. The
Java programming language uses the
rule that the most specific method is
chosen. The informal intuition is that
one method declaration 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 type error.
The precise definition is as follows.
Let m be a name and suppose that there
are two declarations of methods named
m, each having n parameters. Suppose
that one declaration appears within a
class or interface T and that the
types of the parameters are T1, . . .
, Tn; suppose moreover that the other
declaration appears within a class or
interface U and that the types of the
parameters are U1, . . . , Un. Then
the method m declared in T is more
specific than the method m declared in
U if and only if both of the following
are true:
T can be converted to U by method
invocation conversion. Tj can be
converted to Uj by method invocation
conversion, for all j from 1 to n. A
method is said to be maximally
specific for a method invocation if it
is applicable and accessible and there
is no other applicable and accessible
method that is more specific. If there
is exactly one maximally specific
method, then it is in fact the most
specific method; it is necessarily
more specific than any other method
that is applicable and accessible. It
is then subjected to some further
compile-time checks as described in
§15.12.3.
It is possible that no method is the
most specific, because there are two
or more maximally specific methods. In
this case:
If all the maximally specific methods
have the same signature, then: If one
of the maximally specific methods is
not declared abstract, it is the most
specific method. Otherwise, all the
maximally specific methods are
necessarily declared abstract. The
most specific method is chosen
arbitrarily among the maximally
specific methods. However, the most
specific method is considered to throw
a checked exception if and only if
that exception is declared in the
throws clauses of each of the
maximally specific methods. Otherwise,
we say that the method invocation is
ambiguous, and a compile-time error
occurs.
15.12.2.3 Example: Overloading Ambiguity
Consider the example:
class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
static void test(ColoredPoint p, Point q) {
System.out.println("(ColoredPoint, Point)");
}
static void test(Point p, ColoredPoint q) {
System.out.println("(Point, ColoredPoint)");
}
public static void main(String[] args) {
ColoredPoint cp = new ColoredPoint();
test(cp, cp); // compile-time error
}
}
This example produces an error at compile time. The problem is that there are two declarations of test that are applicable and accessible, and neither is more specific than the other. Therefore, the method invocation is ambiguous.
If a third definition of test were added:
static void test(ColoredPoint p, ColoredPoint q) {
System.out.println("(ColoredPoint, ColoredPoint)");
}
then it would be more specific than the other two, and the method invocation would no longer be ambiguous.
Read More