public class Primitive {
void m(Number b, Number ... a) {} // widening, autoboxing->widening->varargs
void m(byte b, Number ... a) {} // unboxing, autoboxing->widening->varargs
public static void main(String[] args) {
Byte b = 12;
Primitive obj = new Primitive();
obj.m(b, 23);
}
}
I have already searched and found that widening priority is higher than unboxing, so in above method invocation, first method should have been called because second parameter is same for both. But this does not happen. Can u plz explain?
It fails to compile in JDK 1.5, 1.6 and 1.7, but works in JDK 1.8.
I don't think that this is a bug that was fixed. Instead, it's rather an effect of the changes that are related to the method invocation mechanisms for lambda expressions in Java 8.
Most people would probably agree that the section about "Method Invocation Expressions" is by far the most complex incomprehensible part of the Java Language Specification. And there is probably a whole team of engineers concerned with cross-checking and validating this section. So any statement or any attempted reasoning should be taken with a huge grain of salt. (Even when it comes from the aforementioned engineers). But I'll give it a try, to at least flesh out the relevant parts that others may refer to for a further analysis:
Considering the section about
and considering that both methods are "Potentially Applicable Methods" ( JLS7 / JLS8 ), then the relevant subsection is the one about
For JLS 7, it states
(The other conditions are referring to forms of invocation that are not relevant here, e.g. invocations that really use the varargs, or invocations that involve generics)
Referring to the example: A method is applicable for the actual argument expression
b
of typeByte
whenb
can be converted to the respective formal method parameter via method invocation conversion. According to the corresponding section about Method Invocation Conversion in JLS7, the following conversions are allowed:Obviously, there are two methods that are applicable according to this specification:
m(Number b, Number ... a)
is applicable via widening reference conversionm(byte b, Number ... a)
is applicable via unboxing conversionYou mentioned that you "...found that widening priority is higher than unboxing", but this is not applicable here: The conditions listed above do not involve any "priority". They are listed as different options. Even if the first method was
void m(Byte b, Number ... a)
, the "identity conversion" would be applicable, but it would still only count as one possible conversion, and cause an error method due to the ambiguity.So, as far as I understood, this explains why it did not work with JDK7. I did not figure out in detail why it did work with JDK8. But the definition of applicability of variable arity methods changed slighly in Identify Methods Applicable by Variable Arity Invocation in JLS 8:
(I did not yet dive deeper into the definitions of "loose invocation contexts" and the section §15.12.2.2, but this seems to be the crucial difference here)
An aside, once more referring to your statement that you "...found that widening priority is higher than unboxing": This is true for methods that do not involve varargs (and that do not require method invocation conversion at all). If you left out the varags in your example, then the process of finding the matching method would start in Phase 1: Identify Matching Arity Methods Applicable by Subtyping. The method
m(Number b)
would then already be applicable for the parameterByte b
due toByte
being a subtype ofNumber
. There would be no reason to go into Phase 2: Identify Matching Arity Methods Applicable by Method Invocation Conversion. In this phase, the method invocation conversion via unboxing fromByte
tobyte
would apply, but this phase is never reached.