Method overloading with variable arguments (varargs) [duplicate] - java

This question already has answers here:
Varargs Java Ambiguous Call
(2 answers)
Closed 7 years ago.
I am surprised by seeing the output of this code :
public class File
{
public static void main(String[] args)
{
movie();
}
static void movie(double... x)
{
System.out.println("No varargs");
}
static void movie(int... x)
{
System.out.println("One argument");
}
}
It outputs,
One argument
Why is it so ?
I thought that this code would not compile because the call to movie() is ambiguous, but it runs fine and outputs One argument.
If I modify the code to:
public class File
{
public static void main(String[] args)
{
movie();
}
static void movie(boolean... x) //Changed the parameter type to boolean from double
{
System.out.println("No varargs");
}
static void movie(int... x)
{
System.out.println("One argument");
}
}
There is an error message.
Why does the first code run fine, but the second gives an error?

This behaviour is due to the fact that int is more specific than double while there is no such comparison between int and boolean.
As specified in the JLS section 15.12.2.5 (emphasis mine):
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, 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.
What more specific actually means is later defined with subtyping:
A type S is more specific than a type T for any expression if S <: T.
This means that S is more specific than T is S is a subtype of T. For primitive types, this comes down to the following properties:
double > float
float > long
long > int
int > char
int > short
short > byte
Notice that boolean is not there.
As such,
public static void main(String[] args) {
movie();
}
static void movie(int... x) { }
static void movie(short... x) { }
static void movie(double... x) { }
static void movie(byte... x) { }
compiles and movie(byte... x) will be called because it is the most specific.
However,
public static void main(String[] args) {
movie();
}
static void movie(int... x) { }
static void movie(boolean... x) { }
does not compile because boolean cannot be compared to int.

The informal intuition is that one method is more specific than
another if any invocation handled by the first method could be passed
on to the other one without a compile-time error.
Consider movie(int...x) as M1 and movie(double...x) as M2.
Method M1 is more specific than M2 because we can call method M2 with the same inputs given to the method M1 directly without any compile time errors.
So, invocation on first method M1 is defineitly handled by the M2. Because double can handle int without any problem.
But we can not invoke M1 with the same inputs given to the method M2 and that is easy to understand.
Let's check following example,
public class Test {
public static void main(String[] args) {
movie();
}
static void movie(int... x) {
System.out.println("One argument");
}
static void movie(short... x) {
System.out.println("Short argument");
}
}
OUTPUT
Short argument
Because here short is more specific than int for method call movie().
On the other hand for boolean method call movie(); raise the confusion, because compiler can not decide which method to call because in this case there is no such point of more specific method.

I think the reason is automatic type promotion in java. By default, the type of expression is promoted to Integer type or Long type (depending upon the range) if your expression does not contain floating point.
So in first case the void expression is simply resolved to int varargs as the Integer wins the contest due to the absence of floating point value in the expression.
In second case however, compiler can not decide what to call, i.e from among the overloaded methods it can not decide which one to call with no-args.

Answer:
1. If we use objects of datatypes like int as Integer it also gives the same error. So the reason is it does not work with Object types and gives errors if no argument is specified for the function.
2. As vararg takes an argument as an Array of a given type. So that's why if you pass no arguments it takes it as an array with zero arguments and executes the integer function because it got one of the functions with an integer argument.
3. It when you put float and double for two different functions and no pass any argument then it executes the float argument function.
Solution:
Do not use object types like String, Integer, Float, Double, Boolean, and Char. Instead use int, float, double, and boolean if want
to execute one of the functions with no arguments passing.
So in the given example we need to specifiy a boolean argument for the Boolean object of non-primitive data type true or false.
public class File
{
public static void main(String[] args)
{
movie(true);
}
static void movie(Boolean...x) //Changed the parameter type to boolean from double
{
System.out.println("No varargs"+x);
}
static void movie(int...x)
{
System.out.println("One argument");
}
}

Related

Java overloaded methods and null parameter [duplicate]

This question already has answers here:
How is an overloaded method chosen when a parameter is the literal null value?
(8 answers)
Closed 4 years ago.
I have the two following overloaded methods:
public static void test(Integer n) {
System.out.println("Integer");
}
public static void test(Number n) {
System.out.println("Number");
}
when I invoke the test method with null, something like that:
public static void main(String[] args) throws Exception {
test(null);
}
I receive:
Integer
Could you please explain why do I receive the Integer and not a compilation error because it is really not obvious with null parameter what test method should be executed?
The <null> type is an acceptable value for both Integer and Number so both are possible overloads.
Java then chooses the most specific overload.
Since Integer is a subtype of Number, it is more specific.
See 15.12.2. Compile-Time Step 2: Determine Method Signature:
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.
And the relative specificity of Integer and Number falls out of this rather opaque spec language in §15.12.2.5:
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).

Why does the following casting with method reference not produce a compilation error? [duplicate]

This question already has answers here:
Why does a Java method reference with return type match the Consumer interface?
(2 answers)
Closed 4 years ago.
public class SomeClass{
public static int someFunction(int a) {
return a;
}
public static void main(String[] args) {
Consumer<Integer> c = SomeClass::someFunction;
}
}
I'm not getting why: Consumer<Integer> c = SomeClass::someFunction;
is not producing a compilation error, since the function someFunction is a method with return value, and Consumer is representing methods with no return value
From the spec:
If the body of a lambda is a statement expression (that is, an
expression that would be allowed to stand alone as a statement), it is
compatible with a void-producing function type; any result is simply
discarded.
Same is true for method references.
It's more flexible that way. Suppose it was a compiler error to not use a return value when you called a method normally - that would be incredibly annoying. You'd end up having to use fake variables you didn't care about in some cases.
public class SomeClass
{
public static int someFunction(int a) {
return a;
}
public static void main(String[] args) {
someFunction(3); // "error" - ignoring return type
int unused = someFunction(3); // "success"
}
}
If you want a the full formal definition of what is acceptable, see 15.13.2. Type of a Method Reference.
This is called special void compatibility rule. For example how many times have you actually cared about List#add return type? Even if it does return true/false.
Pretty much the same thing here, you can invoke a method, but ignore its result. If you re-write your consumer as a lambda expression, it makes more sense:
Consumer<Integer> c = x -> {
SomeClass.someFunction(x);
return;
}
If I remember correctly from the JLS there are only some types that are allowed for this.
increment/decrement operations
method invocation
assignment
instance creation

Primitive vararg parameters in method overloading

Primitives are at it again, breaking rules, I learned before. Well not technically primitive but composed of them.
I learned that whenever there's no method more specific than rest, compile time error occurs as it happens here.
public static void caller(){
z5(); // Error. Neither Integer, nor String is more specific
z5(null); // Error for the same reason
}
public static void z5(Integer...integers){
System.out.println("Integer z5 called");
}
public static void z5(String...strings){
System.out.println("String z5 called");
}
Now comes primitives into the picture.
public static void caller(){
z1(null); // Error cuz [I, [J, [F all are subclass of Object.
z1(); // SURPRISINGLY works and calls the int one. WHY?
}
public static void z1(int...integers){
System.out.println("int z1 called");
}
public static void z1(long...longs){
System.out.println("long z1 called");
}
public static void z1(float...floats){
System.out.println("float z1 called");
}
Expected compile time errors occurs here.
public static void caller(){
z1(null); // Error
z1(); // Error
}
public static void z1(int...integers){
System.out.println("int z1 called");
}
public static void z1(boolean...bools){
System.out.println("bool z1 called");
}
Now my question is, int[], float[], or any array of primitives are not primitive types then Why are they treated differently than other reference types?
--UPDATE--
#john16384 You don't think I read your "Possible duplicate" Varargs in method overloading in Java
Top answer there says You cannot combine var-args, with either widening or boxing. Besides I forgot to mention, OP's code posted there, works fine on my jdk 7.
What exactly is going on which works for (int...is) & (float...fs) but not for (Integer...is) & (Float...fs) and not for (int...is) & (boolean...bool)
Quote from the JLS about varargs invocations when multiple methods are applicable:
15.12.2.5. Choosing the Most Specific Method
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 error. In cases such as an
explicitly typed lambda expression argument (§15.27.1) or a variable
arity invocation (§15.12.2.4), some flexibility is allowed to adapt
one signature to the other.
The important part here is how methods are defined to be more specific. It basically says that int... is more specific than long... because any values you could pass to the first method could also be passed to the second method.
This will also apply to the case where you pass no arguments. int... will be the most specific (it will even see byte... as more specific!).
public static void main(String[] args) {
bla();
}
private static void bla(long... x) {}
private static void bla(int... x) {}
private static void bla(short... x) {}
private static void bla(byte... x) {} // <-- calls this one
The reason you get an error when also creating an overload boolean... is that it now is ambigious which one to call, and the compiler stops before getting to the point where it has to pick the most specific method.

Method overload resolution in java

Here is what I know about overload resolution in java:
The process of compiler trying to resolve the method call from given
overloaded method definitions is called overload resolution. If the
compiler can not find the exact match it looks for the closest match
by using upcasts only (downcasts are never done).
Here is a class:
public class MyTest {
public static void main(String[] args) {
MyTest test = new MyTest();
Integer i = 9;
test.TestOverLoad(i);
}
void TestOverLoad(int a){
System.out.println(8);
}
void TestOverLoad(Object a){
System.out.println(10);
}
}
As expected the output is 10.
However if I change the class definition slightly and change the second overloaded method.
public class MyTest {
public static void main(String[] args) {
MyTest test = new MyTest();
Integer i = 9;
test.TestOverLoad(i);
}
void TestOverLoad(int a){
System.out.println(8);
}
void TestOverLoad(String a){
System.out.println(10);
}
}
The output is 8.
Here I am confused. If downcasting was never to be used, then why did 8 get printed at all? Why did compiler pick up the TestOverLoad method which takes int as an argument which is a downcast from Integer to int?
The compiler will consider not a downcast, but an unboxing conversion for overload resolution. Here, the Integer i will be unboxed to an int successfully. The String method isn't considered because an Integer cannot be widened to a String. The only possible overload is the one that considers unboxing, so 8 is printed.
The reason that the first code's output is 10 is that the compiler will consider a widening reference conversion (Integer to Object) over an unboxing conversion.
Section 15.12.2 of the JLS, when considering which methods are applicable, states:
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 [...]
In Java, resolving methods in case of method overloading is done with the following precedence:
1. Widening
2. Auto-boxing
3. Var-args
The java compiler thinks that widening a primitive parameter is more desirable than performing an auto-boxing operation.
In other words, as auto-boxing was introduced in Java 5, the compiler chooses the older style(widening) before it chooses the newer style(auto-boxing), keeping existing code more robust. Same is with var-args.
In your 1st code snippet, widening of reference variable occurs i.e, Integer to Object rather than un-boxing i.e, Integer to int. And in your 2nd snippet, widening cannot happen from Integer to String so unboxing happens.
Consider the below program which proves all the above statements:
class MethodOverloading {
static void go(Long x) {
System.out.print("Long ");
}
static void go(double x) {
System.out.print("double ");
}
static void go(Double x) {
System.out.print("Double ");
}
static void go(int x, int y) {
System.out.print("int,int ");
}
static void go(byte... x) {
System.out.print("byte... ");
}
static void go(Long x, Long y) {
System.out.print("Long,Long ");
}
static void go(long... x) {
System.out.print("long... ");
}
public static void main(String[] args) {
byte b = 5;
short s = 5;
long l = 5;
float f = 5.0f;
// widening beats autoboxing
go(b);
go(s);
go(l);
go(f);
// widening beats var-args
go(b, b);
// auto-boxing beats var-args
go(l, l);
}
}
The output is:
double double double double int,int Long,Long
Just for reference, here is my blog on method overloading in Java.
P.S: My answer is a modified version of an example given in SCJP.
widening beats boxing, boxing beats var-args. In your example, the widening cannot happen, so the boxing it's applied and Integer is unboxed. Nothing unordinary.
Actually in the second example no downcasting is occurred. There occurred the following thing -
1. Integer is unwrapped/unboxed to primitive type int.
2. Then the TestOverLoad(int a) method is called.
In main method you declare Integer like -
Integer i = 9;
Then call -
test.TestOverLoad(i);
Whereas, you have 2 overloaded version of TestOverLoad() -
TestOverLoad(int a);
TestOverLoad(String a);
Here the second overloaded version of TestOverLoad() takes completely different argument String. Thats why Integer i is unboxed to a primitive type int and after that the first overloaded version is called.
All objects in Java extend the class Object, including the class Integer. These two class have the following relationship: Integer "is a(n)" Object because Integer extends Object. In your first example, the method with Object parameter is used.
In the second example, no methods are found that accept an Integer. In this case Java uses what is called auto-unboxing to resolve the Integer wrapper class to a primitive int. Thus, the method with the int parameter is used.
While accepted answer of #rgettman leads to very right source, more precisely, §15.12.2.2 and §15.12.2.3 of the JLS Section 15.12.2 discuss the applicability, not the resolution - what the OP asked for. In the example the OP provided both testOverLoad methods are applicable, .i.e. will be successfully resolved in the absence of another one.
Instead, 15.12.2.5. Choosing the Most Specific Method discusses the resolution of the applicable methods.
It reads:
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:
...
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).
So, in the first example, provided by OP, for parameter i of type Integer method testOverLoad(Object a) is more specific than testOverLoad(int a).
This is happening due to Widening and Narrowing Type casting
Widening means a small type can be accommodated in a larger type without any loss of information.
Widening Typecasting is automatic.
That means a byte value can be automatically casted to short, int, long or double.
byte->short->int->float->double
Widens from left to right.
Type Casting in Java
Hope this answers your question!
you can check with one more example :
public class HelloWorld {
void show(String c){
System.out.println("int double overloaded method");
}
void show(Object c){
System.out.println("double int overloaded method");
}
}
here the you will get : double int overloaded method

Java: overloaded method resolution

So I have the following overloaded methods:
private static void foo(short... a)
{
System.out.println("Calling var-len");
}
private static void foo(int a, int b)
{
System.out.println("Calling int-int");
}
private static void foo(int a, double b) //(3)
{
System.out.println("Calling int-double");
}
private static void main (String[] args)
{
foo((short)2, (short)5); //This one outputs "Calling int-int"
}
I know that variable arity method has the lowest priority during method resolution phases, so in this case if I call foo((short)2, (short)4); I would get "Calling int-int".
HOWEVER, if I change method (3) to foo(short a, double b), the variable arity method is picked! (Java 7).
Could anyone explain this?
According to the specification:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2
"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."
Your question is a typical example for numerical promotion in java. It tries to resolve for the exact signature match (short, short), but in your case you don't have that method. Java checks for subsequent compatible data type (say int), it promotes both short to int data types and tries to find a match, it finds it.
If none of the numeric promotion works, it will resolve to variable argument method in your code.
Please read on numeric promotions on below link..
http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html

Categories

Resources