Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)

like image 489
Anurag Awasthi Avatar asked Mar 23 '17 13:03

Anurag Awasthi


1 Answers

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.

like image 51
john16384 Avatar answered Sep 19 '22 15:09

john16384