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
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/
I've come across a weird problem where a method reference to Thread::sleep is ambiguous but a method with the same signature is not.
package test;
public class Test
{
public static void main(String[] args)
{
foo(Test::sleep, 1000L); //fine
foo((FooVoid<Long>)Thread::sleep, 1000L); //fine
foo(Thread::sleep, 1000L); //error
}
public static void sleep(long millis) throws InterruptedException
{
Thread.sleep(millis);
}
public static <P, R> void foo(Foo<P, R> function, P param) {}
public static <P> void foo(FooVoid<P> function, P param) {}
#FunctionalInterface
public interface Foo<P, R> {
R call(P param1) throws Exception;
}
#FunctionalInterface
public interface FooVoid<P> {
void call(P param1) throws Exception;
}
}
I get those 2 errors:
Error:(9, 17) java: reference to foo is ambiguous
both method <P,R>foo(test.Test.Foo<P,R>,P) in test.Test and method <P>foo(test.Test.FooVoid<P>,P) in test.Test match
Error:(9, 20) java: incompatible types: cannot infer type-variable(s) P,R
(argument mismatch; bad return type in method reference
void cannot be converted to R)
The only difference I see is that Thread::sleep is native. Does it change anything? I don't think the overload Thread::sleep(long, int) comes into play here. Why does it happen?
EDIT: Using javac version 1.8.0_111
You can recreate the problem in your own class by adding a method sleep with two arguments to class Test like below:
public static void sleep(long millis) {
}
public static void sleep(long millis, int nanos) {
}
So the problem is really caused by the fact that the method sleep is overloaded.
The JLS indicates that the initial method selection code only looks at the number of type arguments to the functional interface - only in the second phase does it look at the signature of the method inside the functional interface.
JLS 15.13:
It is not possible to specify a particular signature to be matched,
for example, Arrays::sort(int[]). Instead, the functional interface
provides argument types that are used as input to the overload
resolution algorithm (§15.12.2).
(the second-to-last paragraph of this section)
So in the case of Thread::sleep, void sleep(long) potentially matches functional interface FooVoid<P>, while overload void sleep(long, int) potentially matches functional interface Foo<P, R>. That's why you get the "reference to foo is ambiguous" error.
When it tries to go further and see how to match Foo<P, R> with functional method R call(P param1) to the method void sleep(long, int), it finds out that this is not actually possible, and you get another compile error:
test/Test.java:7: error: incompatible types: cannot infer type-variable(s) P,R
foo(Thread::sleep, 1000L); // error
^
(argument mismatch; bad return type in method reference
void cannot be converted to R)
The problem is that both, Thread.sleep and foo, are overloaded. So there is a circular dependency.
In order to find out which sleep method to use, we need to know the target type, i.e. which foo method to invoke
In order to find out which foo method to invoke, we need to know the functional signature of the argument, i.e. which sleep method we have selected
While it’s clear to a human reader that for this scenario only one of the 2×2 combinations is valid, the compiler must follow formal rules that work for arbitrary combinations, therefore, the language designers had to make a cut.
For the sake of usefulness of method references, there is a special treatment for unambiguous references, like your Test::sleep:
JLS §15.13.1
For some method reference expressions, there is only one possible compile-time declaration with only one possible invocation type (§15.12.2.6), regardless of the targeted function type. Such method reference expressions are said to be exact. A method reference expression that is not exact is said to be inexact.
Note that this distinction is similar to the distinction between implicitly typed lambda expressions (arg -> expression) and explicitly typed lambda expressions ((Type arg) -> expression).
When you look at JLS, §15.12.2.5., Choosing the Most Specific Method, you’ll see that the signature of a method reference is only used for exact method references, as when choosing the right foo, the decision for the right sleep method has not made yet.
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:
R₂ is void.
R₁ <: R₂.
R₁ is a primitive type, R₂ is a reference type, and the compile-time declaration for the method reference has a return type which is a primitive type.
R₁ is a reference type, R₂ is a primitive type, and the compile-time declaration for the method reference has a return type which is a reference type.
The above rule has been stated in §15.12.2.5. for non-generic methods, redirecting to §18.5.4 for generic methods (which applies here as your foo methods are generic), containing exactly the same rule with a slightly different wording.
Since the method reference’s signature is not considered when choosing the most specific method, there is no most specific method and the invocation of foo is ambiguous. The second compiler error is the result of the strategy to continue processing the source code and potentially reporting more errors, instead of stopping the compilation right at the first error. One of the two invocations of foo caused an “incompatible types” error, if that invocation was happening, but actually that has been ruled out due to the “ambiguous invocation” error.
Personally I see this as some sort of recursion, somehow like this: we need to resolve the method in order to find the target type, but we need to know the target type in order to resolve the method. This has something to do with a special void compatibility rule, but I admit I do not entirely get it.
Things are even funner when you have something like this:
public static void cool(Predicate<String> predicate) {
}
public static void cool(Function<String, Integer> function) {
}
And try to call it via:
cool(i -> "Test"); // this will fail compilation
And btw if you make your lambda explicit, this will work:
foo((Long t) -> Thread.sleep(t), 1000L);
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.