Who can explain why first method preferable than second?
I know this rules for overloading (except first of all compiler find appropriate args)
widening
autoboxing
var-args
Code:
public class Proba{
public static void show(Object ... args){
System.out.println("Object ...");
}
public static void show(Integer[] ... args){
System.out.println("Integer ...");
}
public static void main(String[] args) {
Integer[] array = {3,2,5,1};
show(array);
}
}
Console : Object ...
The rules of method resolution in Java require that a match be attempted without auto-(un)boxing and variable arity before attempting a match with those features. This ensures source compatibility with language versions that predate those features.
The rules for overload resolution are described in the JLS (§15.12.2):
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.
Deciding whether a method is applicable will, in the case of generic methods
(§8.4.4), require that type arguments be determined. Type arguments may be
passed explicitly or implicitly. If they are passed implicitly, they must be inferred
(§15.12.2.7) from the types of the argument expressions.
If several applicable methods have been identified during one of the three phases
of applicability testing, then the most specific one is chosen, as specified in section
§15.12.2.5.
In your example, there are two candidates during Step 1: the method with an Object[] parameter, and the method with an Integer[][] parameter. The argument type at your call site is Integer[]. Since Object[] is assignable from Integer[], but Integer[][] is not, a single applicable method has been found, and overload resolution halts there. Steps 2 and 3 are never reached in this case.
Mike is correct; there are 3 phases,
15.12.2.2. Phase 1: Identify Matching Arity Methods Applicable by Subtyping
15.12.2.3. Phase 2: Identify Matching Arity Methods Applicable by Method Invocation Conversion
15.12.2.4. Phase 3: Identify Applicable Variable Arity Methods
show(Object[]) is chosen in the first phase, but show(Integer[]...) can only be chosen in the 3rd phase.
If the first method signature is changed to show(Object[] ... args), you'll see the expected result.
If the second method signature is changed to show(Integer ... args), you'll also see the expected result. The method also fits in phase 1, and it is more specific than show(Object...)
If we have
public static void show(Object ... args){
System.out.println("Object ...");
}
static class IntArray{}
public static void show(IntArray ... args){
System.out.println("IntArray ...");
}
show(new IntArray());
it prints the expected IntArray .... Here IntArray is not a subtype of Object[].
This is all too confusing. Programmers usually don't know about these phases; they think about all applicable methods and the most specific one among them. It might have been better if the spec does that too.
Related
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
This question already has answers here:
Ambiguous varargs methods
(4 answers)
Closed 6 years ago.
I do not understand why here in case 1, it is not giving compilation error, contrary in case 2 (varargs), it gives compilation error. Can anyone please elaborate what differences the compiler makes in these two cases? I went through many posts about it, but not able to understand it yet.
Case #1
public class Test {
public void display(int a) {
System.out.println("1");
}
public void display(Integer a) {
System.out.println("2");
}
public static void main(String[] args) {
new Test().display(0);
}
}
The Output is: 1
Case #2
public class Test {
public void display(int... a) {
System.out.println("1");
}
public void display(Integer... a) {
System.out.println("2");
}
public static void main(String[] args) {
new Test().display(0);
}
}
Compilation Error:
The method display(int[]) is ambiguous for the type Test
In your first example the display(int) method is invoked in strict invocation context while display(Integer) is invoked in loose invocation context (since auto-boxing is required). Thus the compiler chooses the display(int) method according to JLS. Invocation contexts are explained here JLS 5.3. Invocation Contexts
In the second example both methods are invoked in loose invocation context thus the compiler needs to find the most specific method JLS 15.12.2.5 Choosing the Most Specific Method. Since int is not a subtype of Integer there is no most specific method and the compiler throws a compilation error.
You can find my explanation for a similar compilation error here Method overload ambiguity with Java 8 ternary conditional and unboxed primitives
The parts that apply to this case:
Identifying applicable methods is divided into 3 phases.
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.
For the first example only the display(int) method is matched on the first phase thus it is chosen. For the second example both methods are matched on the 3rd phase thus the Choosing the Most Specific Method algorithm comes into play JLS 15.12.2.5 Choosing the Most Specific Method:
m2 is not generic, and 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.
As mentioned earlier there is no most specific method since int <: Integer does not satisfy.
After java Version 1.5 there is a cool feature introduced named autoboxing which enables compiler to Convert a primitive type to a Wrapper Type. So, during compilation both method will be work same.
public void display(int... a) {
System.out.println("1");
}
public void display(Integer... a) {
System.out.println("2");
}
both the function will be treated as a same method because of autoboxing performs during the compilation . So Be Beware of Autoboxing while overloading method in Java.
More you find Here..
Best Practices Of Method Overloading
I've two versions of addValues, one with vararg parameters.
double addValues(double ... values) {
double result = 0d;
for (double value : values)
result += value;
return result;
}
double addValues(double v1, double v2) {
return v1 + v2;
}
When I call addValues(2, 3) which looks ambiguous to me, why Java selects the addValues(double v1, double v2) version to run the code? How does Java determine which version is 'closer' to the invocation?
Thanks.
This answer gives the relevant section of the Java Language Specification. However it is so complicated that it requires a few examples to explain.
The compiler will always choose a method not needing "variable arity invocation" or auto boxing or auto unboxing if possible. Variable arity invocation is when you invoke a varargs method by passing a parameter list for the last argument (rather than an array).
For example, suppose you have a method with signature
void foo(int... arr)
This is a variable arity invocation...
foo(1, 2, 3);
...but this is not.
foo(new int[] {1, 2, 3});
So in your case, addValues(2, 3) uses the second version as this is not a variable arity invocation.
It is not true to say that the compiler will always favour a method not involving varargs over one that does involve varargs, as this example shows:
public static void bar(int... a) {
System.out.println("Varargs");
}
public static void bar(Object a) {
System.out.println("Object");
}
public static void main(String[] args) {
bar(new int[] {1, 2, 3}); // Prints Varargs
}
In this example, neither choice is a variable arity invocation, but the first version is invoked as it is more specific.
These rules made it possible to change a non-varargs signature
baz(int[] arr)
to a varargs one
baz(int... arr)
without changing the behaviour of any existing program.
The compiler will usually favor a non-varargs overload over a varargs overload. When varargs was added in Java 5, they wanted to add varargs overloads while still remaining backwards compatible, meaning that prior code that was necessarily calling a non-varargs overload would still call that overload and not the varargs overload.
This is explained in the JLS, Section 15.12.2:
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.
(bold emphasis mine)
Code
public class TestOverload {
public TestOverload(int i){System.out.println("Int");}
public TestOverload(char... c){System.out.println("char");}
public static void main(String[] args) {
new TestOverload('a');
new TestOverload(65);
}
}
Output
Int
Int
Is it expected behaviour? If so, then why? I am expecting: char, Int
Note: I am using Java 8
Methods with varargs (...) have the lowest priority when the compiler determines which overloaded method to choose. Therefore TestOverload(int i) is chosen over TestOverload(char... c) when you call TestOverload with a single char parameter 'a', since a char can be automatically promoted to an int.
JLS 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.
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.
EDIT:
It you wish to force the compiler to call the TestOverload(char... c) constructor, you can pass to the constructor call a char[] :
new TestOverload (new char[] {'a'});
Yes, it is expected behaviour. The precedence for method calling goes like this :
Widending
Boxing
Varargs
Below is excerpt from Java docs related to same :-
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.
Solid advice from Joshua Bloch (Effective Java, 2nd Ed):
"only choose as arguments for an overloaded method those that have -radically- different types."
An object with a radically different type is one that can not reasonably be cast into another of the argument types. Following this rule can potentially save you hours of debugging a mysterious error that can happen when the compiler chooses at compile time the method overloading that you did not expect.
Your lines of code violate this rule and open the door for bugs:
public TestOverload(int i){System.out.println("Int");}
public TestOverload(char... c){System.out.println("char");}
A char is interconvertible with an int and so the only way you can predict what will happen with the invocations is to go to the Java Language Specification and read the somewhat arcane rules about how overloadings are resolved.
Luckily, this situation shouldn't need JLS research. If you have arguments that are not radically different from each other, probably the best option is to not overload. Give the methods different names so that there is no possibility for error or confusion on the part of anyone who may need to maintain the code.
Time is money.
I took the code from this link and modified some parts of it:
public static void main(String[] args) {
Byte i = 5;
byte k = 5;
aMethod(i, k);
}
//method 1
static void aMethod(byte i, Byte k) {
System.out.println("Inside 1");
}
//method 2
static void aMethod(byte i, int k) {
System.out.println("Inside 2");
}
//method 3
static void aMethod(Byte i, Byte k) {
System.out.println("Inside 3 ");
}
//method 4
static void aMethod(Byte i, Byte ... k) {
System.out.println("Inside 4 ");
}
The compiler gives error (The method is ambiguous for the type Overloading) for methods 1, 2 and 3 but not 4 (why?)
The answer lies in the mechanism which java uses to match method calls to method signatures. The mechanism is done in three phases, in each phase if it finds matching method it stops:
+phase one: use widening to find matching method (no matching methods found)
+phase two: (also) use boxing/unboxing to find matching method (method 1,2 and 3 match)
+phase three: (also) use var args (method 4 matches!)
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/