15

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?

akash
  • 22,664
  • 11
  • 59
  • 87
Frosted Cupcake
  • 1,909
  • 2
  • 20
  • 42
  • 3
    I can only speculate: well, varargs are de-segurized into arrays. So double...x becomes double[] x, while int...y becomes int[] x. Arrays in Java are reinified, so both double and int are numbers, so they have parent "type" (are compatible) and it calls the smaller (and here I have no idea why). When you change one to boolean it gets lost as which one to call and throws the error. – eribeiro Aug 30 '15 at 05:41

4 Answers4

10

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.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • 1
    It says, "One applicable method m1 is more specific than another applicable method m2, **for an invocation with argument expressions e1, ..., ek,** ..." The **bold** line is kind of confusing me. It says invocation with argument expressions. But in this case `movie()` call has no arguments. – Wololo Aug 30 '15 at 05:54
7

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.

akash
  • 22,664
  • 11
  • 59
  • 87
0

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.

akash
  • 22,664
  • 11
  • 59
  • 87
Shubham Chaurasia
  • 2,472
  • 2
  • 15
  • 22
0

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:

  1. 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.
  2. 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");
    }
}
akash
  • 22,664
  • 11
  • 59
  • 87
vicky
  • 412
  • 4
  • 18