Method Overloading with Widening, Autoboxing and Varargs - Quiz
Total: 5 questions
1. What are the three phases the Java compiler goes through when it picks an overloaded method (JLS 15.12.2)?
What are the three phases the Java compiler goes through when it picks an overloaded method (JLS 15.12.2)?
The compiler moves on to the next phase only if no applicable method was found in the current one:
1. Strict invocation - exact match, primitive widening (byte → int → long) and reference widening (Integer → Object). Boxing, unboxing and varargs expansion are not allowed.
2. Loose invocation - everything from phase 1 plus boxing and unboxing (boxing may be followed by reference widening, unboxing by primitive widening).
3. Variable arity invocation - everything from phase 2 plus varargs expansion: separate arguments are collected into an array.
If several methods apply in the same phase, the most specific one is chosen; if there is none, the call fails with reference to ... is ambiguous. Hence the rule: widening beats boxing, and boxing beats varargs.
2. A class has go(long x) and go(Integer x). Which method is called for int i = 5; go(i);? And which one is called for an Integer argument when the methods are go(long) and go(Object)?
A class has go(long x) and go(Integer x). Which method is called for int i = 5; go(i);? And which one is called for an Integer argument when the methods are go(long) and go(Object)?
For an int, go(long) is called: primitive widening int → long is allowed already in phase 1, while boxing to Integer belongs to phase 2, which is never reached. This keeps code written before autoboxing appeared in Java 5 working the same way.
For an Integer argument with go(long) and go(Object), go(Object) is called: reference widening Integer → Object is also phase 1, while go(long) would need unboxing (phase 2).
3. Why can a byte value be passed to a method with an Object parameter but not to one with a Long parameter? Can a Short be passed to a method with an int parameter?
Why can a byte value be passed to a method with an Object parameter but not to one with a Long parameter? Can a Short be passed to a method with an int parameter?
The compiler can box first and then widen the reference: byte → Byte → Object. The reverse chain - widen the primitive first and then box it (byte → long → Long) - is not allowed: incompatible types: byte cannot be converted to Long. One wrapper cannot be widened to another either: Integer is not a Long, wrapper classes are not related by inheritance. To pass an int to Long, make the argument a long beforehand: go(5L), go((long) x) or go(Long.valueOf(x)).
A Short can be passed to int: the wrapper is unboxed and the resulting primitive is widened (Short → short → int), which is phase 2.
4. A class has invade(short ships) and invade(short... ships). Why does the call invade(7) not compile, and how do you fix it?
A class has invade(short ships) and invade(short... ships). Why does the call invade(7) not compile, and how do you fix it?
The literal 7 is an int, and in a method call, unlike in an assignment (short s = 7;), a constant is not narrowed to short or byte. Neither invade(short) nor invade(short...) accepts an int in any of the three phases, so javac reports no suitable method found for invade(int).
The fix is an explicit cast: invade((short) 7). Then invade(short) applies in phase 1, the varargs method loses, and the call returns "a few".
5. When does overloading produce the error reference to go is ambiguous? Which method is called for go(null)?
When does overloading produce the error reference to go is ambiguous? Which method is called for go(null)?
The error appears when several methods apply in the same phase and none of them is more specific than the others. Example: with go(long, int) and go(int, long), the call go(1, 1) fits both in phase 1 (either the first or the second argument is widened), and the compiler does not pick one at random. The fix is to cast an argument to the exact type (go(1L, 1), Integer.valueOf(1)) or to rename the methods.
null fits any reference parameter already in phase 1, so the most specific type wins: with go(Object) and go(String), go(String) is called; with go(String) and go(Integer), the call is ambiguous because the types are not related by inheritance; with go(int) and go(Integer), go(Integer) is called because a primitive cannot be null.