Java varargs method overloading compiler error - ambiguity? - java

So, today I've been testing Java's overloading techniques and I've come across ambiguity which I can't explain. Basically, when there is a vararg method with primitive and its corresponding wrapper the compiler complains and can't decide which one to choose and I don't understand why? It's easy for human to decide and not for compiler?
Here is the fragment which works for non-vararg parameters:
public static void main(String[] args)
{
int a = 14;
Integer b = new Integer(14);
stuff(a);
stuff(b);
}
static void stuff(Integer arg) { System.out.println("Integer"); }
static void stuff(int arg) { System.out.println("int"); }
And here comes the vararg which complains and cries like a baby:
public static void main(String[] args)
{
int a = 14;
Integer b = new Integer(14);
stuff(a); // Doesn't compile (ambiguity)
stuff(b); // Doesn't compile (ambiguity)
}
static void stuff(int... arg) { System.out.println("varargs int"); }
static void stuff(Integer... arg) { System.out.println("varargs Integer"); }

Consider the following two hypothetical calls to stuff():
int a = 14;
Integer b = new Integer(14);
stuff(a, b);
stuff(b, a);
How does the compiler even know which method should be called here? Because of autoboxing rules, either call could be referring to either overloaded method.
Update:
My answer is logically correct, or at least on the right track, but for a more formal answer we can refer to this SO question:
Why ambiguous error when using varargs overloading with primitive type and wrapper class?
The two varargs method are invoked in loose invocation context. As a result, the compiler will attempt to find the more specific method via JLS 15.12.2.5 Choosing the Most Specific Method. However, since neither int nor Integer are subtypes of one another, the compiler will throw an error.

The problem is:
java is doing behind the scenes bridge methods (you need to verify that is you need deep info)
AND the important part, vargargs means too YOU CAN JUST NOT PASS ANY PARAMETER, so:
static void stuff(int... arg)
and
static void stuff(Integer... arg)
can both being invoked taking no parameters... so that will create some conflict about what method should the JVM invoke

Related

type conversion at method calling

public class Demo1{
public static void main(String[] args){
show('A','A');
}
public static void show(char c, long a){
System.out.println("long-char");
}
public static void show(char c, int a){
System.out.println("char-int");
}
}
Output : char-int
But when I change the order of parameters in the first show() method (replacing
public static void show(char c, long a){} with public static void show(long a, char c) {}), I get a compilation error.
The compiler says that it is an ambiguous method call, because it is.
The general approach taken for overload resolution is to find the most specific applicable method, given the number and types of the actual parameters.
In the first case, the two methods have char as their first parameter; so it is only down to choosing whether the int or long overload is more specific, given that the actual parameter is a char: it is the int overload which is more specific, because int is narrower than long.
In the second case, one method has char as the first parameter; one method has char as the second parameter. So, given that the actual parameters are both chars, one of the parameters has to be converted (widened) to invoke either of the methods.
The language spec does not define that one is more specific than the other in such a case; they are both considered equally applicable, so the method call is ambiguous, and thus is a compile-time error.

How to tell if a primitive type was cast into an object in Java?

In Java, consider the following piece of code:
int myPrimitiveInt = 5;
Integer myObjectInt = 4;
Object fromPrimitive = myPrimitiveInt;
Object fromObject = myObjectInt;
System.out.println(fromPrimitive.getClass());
System.out.println(fromObject.getClass());
System.out.println(int.class);
And the output:
class java.lang.Integer
class java.lang.Integer
int
What I would like, is a way to also get the output int for the first println.
WHY?, you will ask. Well, for one thing, I would just like to know if something like this is even possible.
But the actual practical reason behind this is an abstraction layer for testing private methods with primitive type arguments via reflection. The minimal code:
package testing;
import java.lang.reflect.Method;
public class Testing {
private static void doStuff(int a) {
System.out.println("primitive: " + ((Object) a).getClass());
}
public static void main(String[] args) throws ReflectiveOperationException {
Reflect.reflect(Testing.class, "doStuff", 10);
}
}
abstract class Reflect {
static Object reflect(Class<?> clazz, String methodName, Object arg) throws ReflectiveOperationException {
Method method = clazz.getDeclaredMethod(methodName, arg.getClass());
method.setAccessible(true);
return method.invoke(null, arg);
}
}
The output:
Exception in thread "main" java.lang.NoSuchMethodException: testing.Testing.doStuff(java.lang.Integer)
at java.lang.Class.getDeclaredMethod(Class.java:2130)
at testing.Reflect.reflect(Testing.java:17)
at testing.Testing.main(Testing.java:11)
Expected output:
primitive: class java.lang.Integer
Or even better (if possible at all):
primitive: int
Note: I know I can do clazz.getDeclaredMethod(methodName, int.class). The whole point of this post is to make this procedure more abstract. Please do not give me answers suggesting to pass the argument types to the reflect method!
What happens when you write Object x = 10
The int is autoboxed, which makes it into an Integer with value 10.
Why can't this be detected afterwards
Because there is no difference between the Integer with value 10 that was autoboxed and another Integer with value 10
How can I get around this
You need to add separate methods to overload for primitive values, these can handle the primitive values, so they do not get autoboxed.
A primitive type is never "cast" to an object.
A primitive may be "autoboxed" into an object, but you can never determine this in code, since autoboxing is just code that is added by the compiler and is indistinguishable from the same code that you might have added by hand.

When passing arguments to methods become ambiguous in Java?

When method overloading is done, I know we can only create methods with the same name if only their method signatures are different.
class Demo{
public static void myMethod(int y, double x){}
public static void myMethod(double x,int y){}
public static void main(String args[]){
byte b=10;
myMethod(b,b);
}
}
Code shown above gives an error saying error: reference to myMethod is ambiguous
This problem occurs because the byte value can be assigned to both int and double types after automatic conversion and it is confusing as to which method the values to be passed right?
Please correct me if i'm wrong..
I tried the following program. I thought this would also give an error but it compiled without an error
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
MyClass.myMethod(100);
}
}
I thought it would also give the same error as earlier but this gave the output as myMethod(int)... so i assumed that since it has a perfectly matching method that can pass the int value, it doesn't give an error..
but what if i make the following changes to the second program above, why doesn't it give an error??
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
byte b=10;
MyClass.myMethod(b);
}
}
the byte can be automatically converted into int and double right? the output was given as myMethod(int)..
shouldn't this be confusing to the compiler and give that error reference to myMethod is ambiguous??
I will not specify and detail all rules used by the compiler to decide which method has to be invoked as it encloses other criteria.
I will only focus on the method parameters criteria that is your question.
In 15.12.2.5. Choosing the Most Specific Method, you have a precious information :
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 type error.
In your example here is the part of the JLS that should answer to your question :
One fixed-arity member method named m is more specific than another
member method of the same name and arity if all of the following
conditions hold:
The declared types of the parameters of the first member method are T1, ..., Tn.
The declared types of the parameters of the other method are U1, ..., Un.
If the second method is generic, then let R1 ... Rp (p ≥ 1) be its type parameters, let Bl be the declared bound of Rl (1 ≤ l ≤ p), let
A1 ... Ap be the type arguments inferred (§15.12.2.7) for this
invocation under the initial constraints Ti << Ui (1 ≤ i ≤ n), and let
Si = Ui[R1=A1,...,Rp=Ap] (1 ≤ i ≤ n).
Otherwise, let Si = Ui (1 ≤ i ≤ n).
For all j from 1 to n, Tj <: Sj.
If the second method is a generic method as described above, then Al <: Bl[R1=A1,...,Rp=Ap] (1 ≤ l ≤ p).
Which should interest you is For all j from 1 to n, Tj <: Sj.
At compile-time, if several applicable methods have been identified, then the most specific one is chosen.
Nevertheless, if more than one method have the maximal specificity with the effective parameter types, the compiler doesn't know what method should be invoked. So it emits a compilation error.
You can find this information in the JLS : 15.12.2. Compile-Time Step 2: Determine Method Signature:
A method is said to be maximally specific for a method invocation if
it is accessible and applicable and there is no other method that is
applicable and accessible that is strictly more specific.
If there is exactly one maximally specific method, then that method is
in fact the most specific method; it is necessarily more specific than
any other accessible method that is applicable. It is then subjected
to some further compile-time checks as described in §15.12.3.
It is possible that no method is the most specific, because there are
two or more methods that are maximally specific. In this case:
- If all the maximally specific methods have override-equivalent (§8.4.2) signatures, then:
If exactly one of the maximally specific methods is not declared
abstract, it is the most specific method.
Otherwise, if all the maximally specific methods are declared
abstract, and the signatures of all of the maximally specific methods
have the same erasure (§4.6), then the most specific method is chosen
arbitrarily among the subset of the maximally specific methods that
have the most specific return type.
However, the most specific method is considered to throw a checked
exception if and only if that exception or its erasure is declared in
the throws clauses of each of the maximally specific methods.
- Otherwise, we say that the method invocation is ambiguous, and a
compile-time > error occurs.
If we apply these rules to your three examples (I changed the order to start with cases that compile fine and finish with the compilation error case).
1) One maximally specific method found
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
MyClass.myMethod(100);
}
}
the compiler sees a method with a perfect matching : myMethod(int i) as the value passed is an int. It compiles fine.
2) One maximally specific method found
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
byte b=10;
MyClass.myMethod(b);
}
}
The compiler sees a method with a more higher specificity than the other one.
An implicit conversion from byte to int is indeed more specific than an implicit conversion from byte to double according to widening primitive conversions rules.
We could check that void myMethod(int i) is more specific than void myMethod(double a)if any invocation handled by the first method can be passed on to the other one without a compile-time type error.
myMethod(3); applied to void myMethod(double x) compiles fine.
But myMethod(double)3); applied to void myMethod(int y) produces a compilation error.
So a unique maximally specific method was found : void myMethod(int i).
The compilation is fine.
3) Unique maximally specific method not found
class Demo{
public static void myMethod(int y, double x){}
public static void myMethod(double x,int y){}
public static void main(String args[]){
byte b=10;
myMethod(b,b);
}
}
The compiler sees two methods where no one has a specificity higher than the other one.
First, in both cases, implicit conversions of the effective parameters types to the declared types of methods parameters is required.
But in both cases, the specificity is the same.
We could check that void myMethod(int y, double x) is as specific as myMethod(double x,int y) if any invocation handled by the any method cannot be passed on to the other one without a compile-time type error.
myMethod(double)3,4); applied to void myMethod(int y, double x) produces a compilation error as an int variable cannot accept a double value.
And myMethod(3,(double)4); applied to void myMethod(double x,int y) produces a compilation error for the same reason.
No unique maximally specific method was found. So the compiler cannot guess which method should be called. A compilation error occurs.
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
MyClass.myMethod(100);
}
}
The output is myMethod1(int) because the default type for java integer literal is integer. So it takes the closest match and calls myMethod(int i).
class MyClass{
public static void myMethod(int i){
System.out.println("myMethod1(int)");
}
public static void myMethod(double a){
System.out.println("myMethod2(int)");
}
}
class Demo{
public static void main(String args[]){
byte b=10;
MyClass.myMethod(b);
}
}
This wouldn't create an ambiguity for compiler as the parameter you are sending is byte, byte is widened to match with the method parameter. Had you declared myMethod(short i), It would have called this instead of myMethod(int i). This is how widening works in java.
byte->short->int->long
class Demo{
public static void myMethod(int y, double x){}
public static void myMethod(double x,int y){}
public static void main(String args[]){
byte b=10;
myMethod(b,b);
}
}
The compiler finds an ambiguity in the above snippet as byte b is widened to int now both the methods have parameter declaration as (double,int) (int,double) so there is an ambiguity as both the declaration has an int variable hence it creates a confusion for the compiler itself. Try to change one of the method declaration to (double,double) and you'll see that it calls the one that hasint` in the declaration. Example of what I am saying is
class Demo{
public static void myMethod(double y, double x){}
public static void myMethod(double x,int y){}
public static void main(String args[]){
byte b=10;
myMethod(b,b);
}
}
In this case it will call myMethod(double x, int y)
For more clearance you can refer JLS
It knows to prefer promotion to int over promotion to double, but you've given it a choice between two methods that would both require a promotion to int, so it doesn't know which one you mean.
To understand this kind of problems you should have below threes in mind:
The compiler always tries to choose the most specific method available with least number of modifications to the arguments.
Java designers have decided that old code should work exactly as it used to work before boxing-unboxing functionality became available.
Widening is preferred to boxing/unboxing (because of the above), which in turn, is preferred over var-args.

When I pass a null, which overloaded function will java run

So i can pass nulls into functions in java. I can also overload functions in java. But consider the following
public static void main(String ... args){
doStuff(null);
}
public static void doStuff(String s){
solveWorldHunger();
}
public static void doStuff(Integer i){
nukeCanada();
}
public static void nukeCanada(){
System.out.println("NO!");
}
public static void solveWorldHunger(){
System.out.println("YAY!");
}
The previous program will always print out YAY no matter what the order of the sorce code...
Can anyone shed some light onto why the jvm consitently decides to run the solveWorldHunger function over the nukeCanada function`?
It's quite clear to the compiler that you can't store null in a primitive int. So, it decides to go with the method which has String as argument.
However, when you change your 2nd method to take Integer as argument, then you will get compiler error. Because both of them are eligible to be invoked with null argument. So, there will be ambiguity.
So, try changing your 2nd method: -
public static void doStuff(String s){
solveWorldHunger();
}
public static void doStuff(Integer i){ // Integer instead of `int`
nukeCanada();
}
And invoke it on null. You will see the compiler error.
So, in that case, you can invoke the appropriate method using typecasting like this: -
doStuff((Integer)null); // Calls Integer version
doStuff((String)null); // Calls String version
Also, note that, if you have your 2nd method take a parameter that is a super type of String, like Object, then there would be no ambiguity, because compiler will now choose the most specific one, i.e., String argument.
So, ambiguity only occurs, when you have two methods with types that are not in the same inheritance hierarchy.
Only your doStuff(String) method can take a NULL argument... the primitive int can never be null. If you had a second method overload that passed an object, you would no longer be able to pass null as a parameter for that method.

On what criterion compiler chooses one of the overloaded method

What is the formal reason of prefering one to method to the other(by compiler)?
Why it chooses first one for bytes etc. I know that int can represent bytes, but float also. Why is it so formally?
public class MethodCurrier {
public void setValue(int wrt){//naglowek
System.out.println("Typ int "+ wrt);
}
public void setValue(float wrt){//naglowek
System.out.println("Typ float "+ wrt);
}
public static void main(String[] args) {
MethodCurrier currier = new MethodCurrier();
currier.setValue(4);//int
currier.setValue(2.3f);//float
currier.setValue('c');//char
currier.setValue((byte)4);
}
}
The Java Language Specification defines this as follows:
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 type error.
In your case, the int method is more specific than the float method, because an int can be implictly converted to a float, but not vice versa.
Because the Java Language Specification says so.

Categories