I noticed today that auto-boxing can sometimes cause ambiguity in method overload resolution. The simplest example appears to be this:
public class Test {
static void f(Object a, boolean b) {}
static void f(Object a, Object b) {}
static void m(int a, boolean b) { f(a,b); }
}
When compiled, it causes the following error:
Test.java:5: reference to f is ambiguous, both method
f(java.lang.Object,boolean) in Test and method
f(java.lang.Object,java.lang.Object) in Test match
static void m(int a, boolean b) { f(a, b); }
^
The fix to this error is trivial: just use explicit auto-boxing:
static void m(int a, boolean b) { f((Object)a, b); }
Which correctly calls the first overload as expected.
So why did the overload resolution fail? Why didn't the compiler auto-box the first argument, and accept the second argument normally? Why did I have to request auto-boxing explicitly?
When you cast the first argument to Object yourself, the compiler will match the method without using autoboxing (JLS3 15.12.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.
If you don't cast it explicitly, it will go to the second phase of trying to find a matching method, allowing autoboxing, and then it is indeed ambiguous, because your second argument can be matched by boolean or Object.
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.
Why, in the second phase, doesn't the compiler choose the second method because no autoboxing of the boolean argument is necessary? Because after it has found the two matching methods, only subtype conversion is used to determine the most specific method of the two, regardless of any boxing or unboxing that took place to match them in the first place (§15.12.2.5).
Also: the compiler can't always choose the most specific method based on the number of auto(un)boxing needed. It can still result in ambiguous cases. For example, this is still ambiguous:
public class Test {
static void f(Object a, boolean b) {}
static void f(int a, Object b) {}
static void m(int a, boolean b) { f(a, b); } // ambiguous
}
Remember that the algorithm for choosing a matching method (compile-time step 2) is fixed and described in the JLS. Once in phase 2 there is no selective autoboxing or unboxing. The compiler will locate all the methods that are accessible (both methods in these cases) and applicable (again the two methods), and only then chooses the most specific one without looking at boxing/unboxing, which is ambiguous here.
The compiler did auto-box the first argument. Once that was done, it's the second argument that's ambiguous, as it could be seen as either boolean or Object.
This page explains the rules for autoboxing and selecting which method to invoke. The compiler first tries to select a method without using any autoboxing at all, because boxing and unboxing carry performance penalties. If no method can be selected without resorting to boxing, as in this case, then boxing is on the table for all arguments to that method.
When you say f(a, b), the compiler is confused as to which function it should reference to.
This is because a is an int, but the argument expected in f is an Object. So the compliler decides to convert a to an Object. Now the problem is that, if a can be converted to an object, so can be b.
This means that the function call can reference to either definitions. This makes the call ambiguous.
When you convert a to an Object manually, the compiler just looks for the closest match and then refers to it.
Why didn't the compiler select the
function that can be reached by "doing
the least possible number of
boxing/unboxing conversions"?
See the following case:
f(boolean a, Object b)
f(Object a , boolean b)
If we call like f(boolean a, boolean b), which function should it select? It ambigous right? Similarly, this will become more complex when a lot of arguments are present. So the compiler chose to give you a warning instead.
Since there is no way to know which one of the functions the programmer really intended to call, the compiler gives an error.
So why did the overload resolution
fail? Why didn't the compiler auto-box
the first argument, and accept the
second argument normally? Why did I
have to request auto-boxing
explicitly?
It didn't accept the second argument normally. Remember that "boolean" can be boxed to an Object too. You could have explicitly cast the boolean argument to Object as well and it would have worked.
See http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448
The cast helps because then no boxing is needed to find the method to call. Without the cast the second try is to allow boxing and then also the boolean can be boxed.
It is better to have clear and understandable specs to say what will happen than to make people guess.
The Java compiler resolves overloaded methods and constructors in phases. In the first phase [§15.12.2.2], it identifies applicable methods by subtyping [§4.10]. In this example, neither method is applicable, because int is not a subtype of Object.
In the second phase [§15.12.2.3], the compiler identifies applicable methods by method invocation conversion [§5.3], which is a combination of autoboxing and subtyping. The int argument can be converted to an Integer, which is a subtype of Object, for both overloads. The boolean argument needs no conversion for the first overload, and can be converted to Boolean, a subtype of Object, for the second. Therefore, both methods are applicable in the second phase.
Since more than one method is applicable, the compiler must determine which is most specific [§15.12.2.5]. It compares the parameter types, not the argument types, and it doesn't autobox them. Object and boolean are unrelated types, so they are considered equally specific. Neither method is more specific than the other, so the method call is ambiguous.
One way to resolve the ambiguity would be to change the boolean parameter to type Boolean, which is a subtype of Object. The first overload would always be more specific (when applicable) than the second.
Related
Given these two constructors:
SomeClass(int... params)
{
// Do things
}
SomeClass(long... otherParams)
{
// Do other things
}
What happens when an object foo is instantiated?
SomeClass foo = new SomeClass();
Is the undefined default constructor somehow called? Or is one of those constructors with an empty array called? If so, what’s the precedent?
I’ve done some basic testing and found that if a constructor without parameters is defined then that will be called. Otherwise, it appears that an ambiguous one is called.
As per this very good answer in "Varargs in method overloading in Java" question below are the rules used by Java compiler for selecting the method signature to invoke. They are based on JLS 5.3. Method Invocation Conversion docs.
Primitive widening uses the smallest method argument possible
Wrapper type cannot be widened to another Wrapper type
You can Box from int to Integer and widen to Object but no to Long
Widening beats Boxing, Boxing beats Var-args.
You can Box and then Widen (An int can become Object via Integer)
You cannot Widen and then Box (An int cannot become Long)
You cannot combine var-args, with either widening or boxing
Because both constructors are var-args (rule 7) the compiler will fall back to other rules and select the method that uses the smallest type (rule 1).
You can confirm this behaviour with following code:
static class SomeClass {
SomeClass(long... value) { System.out.println("Long"); }
SomeClass(int... value) { System.out.println("Int"); }
SomeClass(byte... value) { System.out.println("Byte"); }
}
public static void main(String[] args) throws Exception {
SomeClass o = new SomeClass(); // Byte
}
The precise subtype relation between primitives types used in rule 1 is explained in JLS 4.10.1. Subtyping among Primitive Types.
The following rules define the direct supertype relation among the primitive types:
double >1 float
float >1 long
long >1 int
int >1 char
int >1 short
short >1 byte
Only classes without any explicit constructors at all get a default constructor. For a class that does have one or more constructors explicitly defined, their arity, variable or not, has no bearing. Thus it is reasonably common for a class to have no nullary constructor, and that is in fact the case of your class.
Choosing from among multiple available constructors works the same way as choosing among overloaded methods. First, the available constructors are determined. Then, those that are applicable to the given arguments are identified. Finally, the most specific among the applicable constructors is selected. Details are specified in section 15.12 of JLS10. It is a compile-time error if that process does not result in identifying exactly one constructor.
In your example, both available constructors are applicable to an empty argument list, so it comes down to a question of choosing the most specific. The JLS provides an informal description:
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.
The formal rules revolve around the types of the formal parameters, and account for formal type / subtype relationships among primitive types, with the end result that SomeClass(int...) is more specific than SomeClass(long...) when both are applicable. The former, then, is the one chosen in your example.
I am trying to understand method overloading, and I have these methods.
public void method(int a){
System.out.println("int a");
}
//implementing interface method
#Override
public void method() {
System.out.println("interface");
}
//varargs
public void method(int ... a){
System.out.println("int ... a");
}
After calling them with these parameters,
int[] a = new int[5];
stack.method();
stack.method(1);
stack.method(5,6);
stack.method(null);
stack.method(a);
I have these results:
interface
int a
int ... a
int ... a
int ... a
As far as I know, the program should not compile, beacuse of ambiguity, but it does anyway. Shouldn't the compiler throw an error?
Eran and Bathsheba have already said why the various ones not using null were chosen.
The rest of the question is: Why does stack.method(null); even compile?
The answer is that it matches the varargs signature, because the varargs method(int...) is effectively the same from the compiler's perspective as method(int[]). Since arrays are referenced by references, null can be used where an int[] is expected.
So:
stack.method();
Exact match for the method() signature in the interface. Not ambiguous with method(int...) because varargs are considered only when others don't match.
stack.method(1);
Matches method(int). Not ambiguous for the same reason as above.
stack.method(5,6);
Matches method(int...) because none of the non-varargs ones matched, but the varargs one did.
stack.method(null);
See earlier explanation.
stack.method(a);
Matches match(int...) for the same reason method(null0 does: Because match(int...) is effectively the same as match(int[]) to the compiler.
Method overloading resolution has three stages. The first and second stages don't consider methods with varargs (also called variable arity methods) as candidates, so only if no matching method without varargs is found, the compiler considers method with varargs as candidates.
Therefore, in the first and second method calls, your void method(int ... a) is ignored, and there is no ambiguity.
15.12.2. Compile-Time Step 2: Determine Method Signature
The second step searches the type determined in the previous step for
member methods. This step uses the name of the method and the argument
expressions to locate methods that are both accessible and applicable,
that is, declarations that can be correctly invoked on the given
arguments.
There may be more than one such method, in which case the most
specific one is chosen. The descriptor (signature plus return type) of
the most specific method is the one used at run time to perform the
method dispatch.
A method is applicable if it is applicable by one of strict invocation
(§15.12.2.2), loose invocation (§15.12.2.3), or variable arity
invocation (§15.12.2.4).
Certain argument expressions that contain implicitly typed lambda
expressions (§15.27.1) or inexact method references (§15.13.1) are
ignored by the applicability tests, because their meaning cannot be
determined until a target type is selected.
Although the method invocation may be a poly expression, only its
argument expressions - not the invocation's target type - influence
the selection of applicable methods.
The process of determining applicability begins by determining the
potentially applicable methods (§15.12.2.1).
The remainder of the process is split into three phases, to ensure
compatibility with versions of the Java programming language prior to
Java SE 5.0. The phases are:
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.
This guarantees that any calls that were valid in the Java programming language before Java SE 5.0 are not considered ambiguous
as the result of the introduction of variable arity methods, implicit
boxing and/or unboxing. However, the declaration of a variable arity
method (§8.4.1) can change the method chosen for a given method method
invocation expression, because a variable arity method is treated as a
fixed arity method in the first phase. For example, declaring
m(Object...) in a class which already declares m(Object) causes
m(Object) to no longer be chosen for some invocation expressions (such
as m(null)), as m(Object[]) is more specific.
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.
This ensures that a method is never chosen through variable arity method invocation if it is applicable through fixed arity method
invocation.
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
A method with a variable argument list is only considered by the compiler once all other possibilities have been exhausted.
These "other possibilities" are considered in the normal way.
Hence in your case there is no ambiguity and so the compiler does not emit an error.
No it is fine there is no ambiguity : passing "(5,6)" is fine because the method expxects many integers , passing "(a)" is also fine because a is an integer array passing"(null)" is also fine beacause null can be cast to any reference type like an integer [] so it can be used where you expect int [];
so all these calls call the third method
public void method(int ... a){
System.out.println("int ... a");
}
the first two method calls are self explanatory they call methods
public void method(){
System.out.println("interface");
}
and
public void method(int a){
System.out.println("int a");
}
respectively
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/
This question already has answers here:
Which overload will get selected for null in Java?
(3 answers)
Closed 9 years ago.
I have the following code snippet:
public static void foo(Object x) {
System.out.println("Obj");
}
public static void foo(String x) {
System.out.println("Str");
}
If I call foo(null) why is there no ambiguity? Why does the program call foo(String x) instead of foo(Object x)?
why the program calls foo(String x) instead of foo(Object x)
That is because String class extends from Object and hence is more specific to Object. So, compiler decides to invoke that method. Remember, Compiler always chooses the most specific method to invoke. See Section 15.12.5 of JLS
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.
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 type error.
However, if you have two methods with parameter - String, and Integer, then you would get ambiguity error for null, as compiler cannot decide which one is more specific, as they are non-covariant types.
The type of null is by definition a subtype of every other reference type. Quote JLS 4.1:
The null reference can always undergo a widening reference conversion to any reference type.
The resolution of the method signature involved in an invocation follows the principle of the most specific signature in the set of all compatible signatures. (JLS 15.12.2.5. Choosing the Most Specific Method).
Taken together this means that the String overload is chosen in your example.
It's calling the most specific method.
Since String is a subclass of Object, String is "more specific" than Object.
When given a choice between two methods where the argument is valid for both parameters, the compiler will always choose the most specific parameter as a match. In this case, null is a literal that can be handled as an Object and a String. String is more specific and a subclass of Object so the compiler uses it.
This question already has answers here:
Which overload will get selected for null in Java?
(3 answers)
Closed 9 years ago.
I have the following code snippet:
public static void foo(Object x) {
System.out.println("Obj");
}
public static void foo(String x) {
System.out.println("Str");
}
If I call foo(null) why is there no ambiguity? Why does the program call foo(String x) instead of foo(Object x)?
why the program calls foo(String x) instead of foo(Object x)
That is because String class extends from Object and hence is more specific to Object. So, compiler decides to invoke that method. Remember, Compiler always chooses the most specific method to invoke. See Section 15.12.5 of JLS
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.
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 type error.
However, if you have two methods with parameter - String, and Integer, then you would get ambiguity error for null, as compiler cannot decide which one is more specific, as they are non-covariant types.
The type of null is by definition a subtype of every other reference type. Quote JLS 4.1:
The null reference can always undergo a widening reference conversion to any reference type.
The resolution of the method signature involved in an invocation follows the principle of the most specific signature in the set of all compatible signatures. (JLS 15.12.2.5. Choosing the Most Specific Method).
Taken together this means that the String overload is chosen in your example.
It's calling the most specific method.
Since String is a subclass of Object, String is "more specific" than Object.
When given a choice between two methods where the argument is valid for both parameters, the compiler will always choose the most specific parameter as a match. In this case, null is a literal that can be handled as an Object and a String. String is more specific and a subclass of Object so the compiler uses it.