I stumbled upon this snippet:
public class ParamTest {
public static void printSum(int a, double b) {
System.out.println("In intDBL " + (a + b));
}
public static void printSum(long a, long b) {
System.out.println("In long " + (a + b));
}
public static void printSum(double a, long b) {
System.out.println("In doubleLONG " + (a + b));
}
public static void main(String[] args) {
printSum(1, 2);
}
}
This will result in a compile error:
Error:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest match
How is this ambiguous? Shouldn't only the second parameter be promoted in this case since the first parameter is already an int? The first param need not be promoted in this case right?
The compilation succeeds if I update the code to add another method:
public static void printSum(int a, long b) {
System.out.println(String.format("%s, %s ", a, b));
}
Let me expand just to clarify. The code below results in ambiguity:
public class ParamTest {
public static void printSum(int a, double b) {
System.out.println("In intDBL " + (a + b));
}
public static void printSum(long a, long b) {
System.out.println("In long " + (a + b));
}
public static void main(String[] args) {
printSum(1, 2);
}
}
Then this code below also results in ambiguity:
public class ParamTest {
public static void printSum(int a, double b) {
System.out.println("In intDBL " + (a + b));
}
public static void printSum(double a, long b) {
System.out.println("In doubleLONG " + (a + b));
}
public static void main(String[] args) {
printSum(1, 2);
}
}
However this one does not result in ambiguity:
public class ParamTest {
public static void printSum(int a, double b) {
System.out.println("In intDBL " + (a + b));
}
public static void printSum(long a, double b) {
System.out.println("In longDBL " + (a + b));
}
public static void main(String[] args) {
printSum(1, 2);
}
}
In a similar way, when you pass an argument to a method, Java can promote one data type to another. For example, If a method has a double parameter and you are passing in an integer, the compiler promotes an integer to a double.
Type Promotion in Expressions Java automatically promotes each byte, short, or char operand to int when evaluating an expression. If one operand is long, float or double the whole expression is promoted to long, float, or double respectively.
Type promotion automatically promotes the lower range value to higher range value. For example, byte variable can be assigned to an int variable. Here byte variable will be type promoted to int. In case, we want to add two numbers which can be byte, short or int, we can use a single method.
Type Promotion Rules All byte and short values are promoted to int . If one operand is a long , the whole expression is promoted to long . If one operand is a float , the entire expression is promoted to float . If any of the operands is double , the result is double .
I think this has something to do with JLS's specific rule about 15.12.2.5. Choosing the Most Specific Method. It states that:
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.
How Java chooses the most specific method is further explained by the text:
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.
In the case of your example, all methods are accessible and applicable to method invocation, therefore, Java needs to determine which of them is most specific.
For these methods, none can be determined to be more specific:
public static void printSum(int a, double b) {
System.out.println("In intDBL " + (a + b));
} // int, double cannot be passed to long, long or double, long without error
public static void printSum(long a, long b) {
System.out.println("In long " + (a + b));
} // long , long cannot be passed to int, double or double, long without error
public static void printSum(double a, long b) {
System.out.println("In doubleLONG " + (a + b));
} // double, long cannot be passed to int, double or long, long without error
The fourth method clears ambiguity precisely because it fulfills the necessary condition to be most specific.
public static void printSum(int a, long b) {
System.out.println(String.format("%s, %s ", a, b));
}
That is, (int, long) can be passed to (int, double), (long, long), or (double, long) without compilation errors.
It is indeed a very interesting question. Let's go through the Java Language Specification step by step.
When the compiler is trying to identify potentially applicable methods, the first thing it does is serching for methods applicable by Strict Invocation.
In your case there is no such methods, so the next step is to find methods applicable by Loose Invocation
At this point all of the methods match, so the most specific method (§15.12.2.5) is chosen among the methods that are applicable by loose invocation.
This is a key moment, so let's look at this closely.
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:
(We are interested in the following case only):
- 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).
Simply put, a method is more specific if all of its parameters types are more specific. And
A type S is more specific than a type T for any expression if S <: T (§4.10).
Expression S <: T
means that S
is a subtype of T
. For primitives we have the following relationship:
double > float > long > int
So let's look at your methods and see which one is more specific than others.
public static void printSum(int a, double b) { // method 1
System.out.println("In intDBL " + (a + b));
}
public static void printSum(double a, long b) { // method 2
System.out.println("In doubleLONG " + (a + b));
}
In this example the first parameter of the method 1 is obviously more specific than the first parameter of the method 2 (if you call them with integer values: printSum(1, 2)
). But the second parameter is more specific for the method 2, because long < double
. So none of these methods is more specific than another. That's why you have an ambiguity here.
In the following example:
public static void printSum(int a, double b) { // method 1
System.out.println("In intDBL " + (a + b));
}
public static void printSum(long a, double b) { // method 2
System.out.println("In longDBL " + (a + b));
}
the first parameter type of the method 1 is more specific than the one in the method 2, because int < long
and the second parameter type is the same for both of them, that's why the method 1 is chosen.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With