Given these two constructors:
SomeClass(int... params)
{
// Do things
}
SomeClass(long... otherParams)
{
// Do other things
}
What happens when an object foo is instantiated?
SomeClass foo = new SomeClass();
Is the undefined default constructor somehow called? Or is one of those constructors with an empty array called? If so, what’s the precedent?
I’ve done some basic testing and found that if a constructor without parameters is defined then that will be called. Otherwise, it appears that an ambiguous one is called.
As per this very good answer in "Varargs in method overloading in Java" question below are the rules used by Java compiler for selecting the method signature to invoke. They are based on JLS 5.3. Method Invocation Conversion docs.
Primitive widening uses the smallest method argument possible
Wrapper type cannot be widened to another Wrapper type
You can Box from int to Integer and widen to Object but no to Long
Widening beats Boxing, Boxing beats Var-args.
You can Box and then Widen (An int can become Object via Integer)
You cannot Widen and then Box (An int cannot become Long)
You cannot combine var-args, with either widening or boxing
Because both constructors are var-args (rule 7) the compiler will fall back to other rules and select the method that uses the smallest type (rule 1).
You can confirm this behaviour with following code:
static class SomeClass {
SomeClass(long... value) { System.out.println("Long"); }
SomeClass(int... value) { System.out.println("Int"); }
SomeClass(byte... value) { System.out.println("Byte"); }
}
public static void main(String[] args) throws Exception {
SomeClass o = new SomeClass(); // Byte
}
The precise subtype relation between primitives types used in rule 1 is explained in JLS 4.10.1. Subtyping among Primitive Types.
The following rules define the direct supertype relation among the primitive types:
double >1 float
float >1 long
long >1 int
int >1 char
int >1 short
short >1 byte
Only classes without any explicit constructors at all get a default constructor. For a class that does have one or more constructors explicitly defined, their arity, variable or not, has no bearing. Thus it is reasonably common for a class to have no nullary constructor, and that is in fact the case of your class.
Choosing from among multiple available constructors works the same way as choosing among overloaded methods. First, the available constructors are determined. Then, those that are applicable to the given arguments are identified. Finally, the most specific among the applicable constructors is selected. Details are specified in section 15.12 of JLS10. It is a compile-time error if that process does not result in identifying exactly one constructor.
In your example, both available constructors are applicable to an empty argument list, so it comes down to a question of choosing the most specific. The JLS provides an informal description:
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.
The formal rules revolve around the types of the formal parameters, and account for formal type / subtype relationships among primitive types, with the end result that SomeClass(int...) is more specific than SomeClass(long...) when both are applicable. The former, then, is the one chosen in your example.
Related
I noticed today that auto-boxing can sometimes cause ambiguity in method overload resolution. The simplest example appears to be this:
public class Test {
static void f(Object a, boolean b) {}
static void f(Object a, Object b) {}
static void m(int a, boolean b) { f(a,b); }
}
When compiled, it causes the following error:
Test.java:5: reference to f is ambiguous, both method
f(java.lang.Object,boolean) in Test and method
f(java.lang.Object,java.lang.Object) in Test match
static void m(int a, boolean b) { f(a, b); }
^
The fix to this error is trivial: just use explicit auto-boxing:
static void m(int a, boolean b) { f((Object)a, b); }
Which correctly calls the first overload as expected.
So why did the overload resolution fail? Why didn't the compiler auto-box the first argument, and accept the second argument normally? Why did I have to request auto-boxing explicitly?
When you cast the first argument to Object yourself, the compiler will match the method without using autoboxing (JLS3 15.12.2):
The first phase (§15.12.2.2) performs
overload resolution without permitting
boxing or unboxing conversion, or the
use of variable arity method
invocation. If no applicable method is
found during this phase then
processing continues to the second
phase.
If you don't cast it explicitly, it will go to the second phase of trying to find a matching method, allowing autoboxing, and then it is indeed ambiguous, because your second argument can be matched by boolean or Object.
The second phase (§15.12.2.3) performs
overload resolution while allowing
boxing and unboxing, but still
precludes the use of variable arity
method invocation.
Why, in the second phase, doesn't the compiler choose the second method because no autoboxing of the boolean argument is necessary? Because after it has found the two matching methods, only subtype conversion is used to determine the most specific method of the two, regardless of any boxing or unboxing that took place to match them in the first place (§15.12.2.5).
Also: the compiler can't always choose the most specific method based on the number of auto(un)boxing needed. It can still result in ambiguous cases. For example, this is still ambiguous:
public class Test {
static void f(Object a, boolean b) {}
static void f(int a, Object b) {}
static void m(int a, boolean b) { f(a, b); } // ambiguous
}
Remember that the algorithm for choosing a matching method (compile-time step 2) is fixed and described in the JLS. Once in phase 2 there is no selective autoboxing or unboxing. The compiler will locate all the methods that are accessible (both methods in these cases) and applicable (again the two methods), and only then chooses the most specific one without looking at boxing/unboxing, which is ambiguous here.
The compiler did auto-box the first argument. Once that was done, it's the second argument that's ambiguous, as it could be seen as either boolean or Object.
This page explains the rules for autoboxing and selecting which method to invoke. The compiler first tries to select a method without using any autoboxing at all, because boxing and unboxing carry performance penalties. If no method can be selected without resorting to boxing, as in this case, then boxing is on the table for all arguments to that method.
When you say f(a, b), the compiler is confused as to which function it should reference to.
This is because a is an int, but the argument expected in f is an Object. So the compliler decides to convert a to an Object. Now the problem is that, if a can be converted to an object, so can be b.
This means that the function call can reference to either definitions. This makes the call ambiguous.
When you convert a to an Object manually, the compiler just looks for the closest match and then refers to it.
Why didn't the compiler select the
function that can be reached by "doing
the least possible number of
boxing/unboxing conversions"?
See the following case:
f(boolean a, Object b)
f(Object a , boolean b)
If we call like f(boolean a, boolean b), which function should it select? It ambigous right? Similarly, this will become more complex when a lot of arguments are present. So the compiler chose to give you a warning instead.
Since there is no way to know which one of the functions the programmer really intended to call, the compiler gives an error.
So why did the overload resolution
fail? Why didn't the compiler auto-box
the first argument, and accept the
second argument normally? Why did I
have to request auto-boxing
explicitly?
It didn't accept the second argument normally. Remember that "boolean" can be boxed to an Object too. You could have explicitly cast the boolean argument to Object as well and it would have worked.
See http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448
The cast helps because then no boxing is needed to find the method to call. Without the cast the second try is to allow boxing and then also the boolean can be boxed.
It is better to have clear and understandable specs to say what will happen than to make people guess.
The Java compiler resolves overloaded methods and constructors in phases. In the first phase [§15.12.2.2], it identifies applicable methods by subtyping [§4.10]. In this example, neither method is applicable, because int is not a subtype of Object.
In the second phase [§15.12.2.3], the compiler identifies applicable methods by method invocation conversion [§5.3], which is a combination of autoboxing and subtyping. The int argument can be converted to an Integer, which is a subtype of Object, for both overloads. The boolean argument needs no conversion for the first overload, and can be converted to Boolean, a subtype of Object, for the second. Therefore, both methods are applicable in the second phase.
Since more than one method is applicable, the compiler must determine which is most specific [§15.12.2.5]. It compares the parameter types, not the argument types, and it doesn't autobox them. Object and boolean are unrelated types, so they are considered equally specific. Neither method is more specific than the other, so the method call is ambiguous.
One way to resolve the ambiguity would be to change the boolean parameter to type Boolean, which is a subtype of Object. The first overload would always be more specific (when applicable) than the second.
Let's assume I have following code:
// Method acception generic parameter
public static <T> T foo(T para) {
return para;
}
// Method accepting Integer parameter
public static Integer foo(Integer para) {
return para + 1;
}
// Method accepting Number parameter
public static Number foo(Number para) {
return para.intValue() + 2;
}
public static void main(String[] args) {
Float f = new Float(1.0f);
Integer i = new Integer(1);
Number n = new Integer(1);
String s = "Test";
Number fooedFloat = foo(f); // Uses foo(Number para)
Number fooedInteger = foo(i); // Uses foo(Integer para)
Number fooedNumber = foo(n); // Uses foo(Number para)
String fooedString = foo(s); // Uses foo(T para)
System.out.println("foo(f): " + fooedFloat);
System.out.println("foo(i): " + fooedInteger);
System.out.println("foo(n): " + fooedNumber);
System.out.println("foo(s): " + fooedString);
}
The output looks the following:
foo(f): 3
foo(i): 2
foo(n): 3
foo(s): Test
Now the question(s):
foo(n) calls foo(Number para), most probably because n is defined as Number, even though it has an Integer assigned to it. So am I right in the assumption that the decision, which of the overloaded methods is taken happens at compile-time, without dynamic binding? (Question about static and dynamic binding)
foo(f) uses foo(Number para), while foo(i) uses foo(Integer para). Only foo(s) uses the generic version. So the compiler always looks if there is a non-generic implementation for the given types, and only if not it falls back to the generic version? (Question about generics)
Again, foo(f) uses foo(Number para), while foo(i) uses foo(Integer para). Yet, Integer i would also be a Number. So always the method with the "outer-most" type within the inheritance tree is taken? (Question about inheritance)
I know these are a lot questions, and the example is not taken from productive code, yet I just would like to know what "happens behind" and why things happen.
Also any links to the Java documentation or the Java specification are really appreciated, I could not find them myself.
The rules of determining which method signature to call at compile-time are explained in the language specification. Specifically important is the section on choosing the most specific method. Here are the parts related to your questions:
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.
...
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:
m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by §18.5.4.
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).
...
A type S is more specific than a type T for any expression if S <: T (§4.10).
In this case, Integer is more specific than Number because Integer extends Number, so whenever the compiler detects a call to foo that takes a variable declared of type Integer, it will add an invocation for foo(Integer).
More about the first condition related to the second method being generic is explained in this section. It's a little verbose but I think the important part is:
When testing that one applicable method is more specific than another (§15.12.2.5), where the second method is generic, it is necessary to test whether some instantiation of the second method's type parameters can be inferred to make the first method more specific than the second.
...
Let m1 be the first method and m2 be the second method. Where m2 has type parameters P1, ..., Pp, let α1, ..., αp be inference variables, and let θ be the substitution [P1:=α1, ..., Pp:=αp].
...
The process to determine if m1 is more specific than m2 is as follows:
...
If Ti is a proper type, the result is true if Si is more specific than Ti for ei (§15.12.2.5), and false otherwise. (Note that Si is always a proper type.)
Which basically means that foo(Number) and foo(Integer) are both more specific than foo(T) because the compiler can infer at least one type for the generic method (e.g. Number itself) that makes foo(Number) and foo(Integer) more specific (this is because Integer <: Number and Number <: Number) .
This also means that in your code foo(T) is only applicable (and inherently the most specific method since it's only the one applicable) for the invocation that passes a String.
Am I right in the assumption that the decision, which of the overloaded methods is taken happens at compile-time, without dynamic binding?
Yes, Java chooses among available overloads of a method at compile time, based on the declared types of the arguments, from among the alternatives presented by the declared type of the target object.
Dynamic binding applies to choosing among methods having the same signature based on the runtime type of the invocation target. It has nothing directly to do with the runtime types of the actual arguments.
So the compiler always looks if there is a non-generic implementation for the given types, and only if not it falls back to the generic version?
Because of type erasure, the actual signature of your generic method is
Object foo(Object);
Of the argument types you tested, that is the best match among the overloaded options only for the String.
So always the method with the "outer-most" type within the inheritance tree is taken?
More or less. When selecting among overloads, the compiler chooses the alternative that best matches the declared argument types. For a single argument of reference type, this is the method whose argument type is the argument's declared type, or its nearest supertype.
Things can get dicey if Java has to choose among overloads of multiple-argument methods, and it doesn't have an exact match. When there are primitive arguments, it also has to consider the allowed argument conversions. The full details take up a largish section of the JLS.
So, it is pretty simple:
1) Yes, the decision is made at compile-time. The compiler chooses the method with the most specific matching type. So the compiler will choose the Number version when the variable you pass as an argument is declared as a Number, even if it is an Integer at run-time. (If the compiler finds two "equally matching" methods, an ambiguous method error will make the compilation fail)
2) At run-time, there are no generics, everything is just an Object. Generics are a compile-time and source-code feature only. Therefore the compiler must do the best he can, because the VM surely can not.
This question already has answers here:
Auto boxing and primitive types to match method signature
(5 answers)
Why are contravariant parameter types in Java not allowed for overriding?
(1 answer)
Closed 7 years ago.
I have started with JAVA programming recently and have one question to ask.
Let's say I have one SuperClass and one SubClass which extends SuperClass and try to Override a method defined in SuperClass as follows:
public class SuperClass{
public void method1(int val){
System.out.println("This is method in SuperClass.Val is:" + val);
}
}
I try to extend the SuperClass in my SubClass and override the method with only one exception, instead of type int declared in method1, I use the type for the argument as Integer, as follows:
public class SubClass extends SuperClass{
#Override
public void method1(Integer val){ ///compiler gives an error
}
}
This declaration of SubClass method is not allowed by the compiler(I am using eclipse IDE). Why is this so? Integer is essentially wrapper of int then why such a declaration is prohibited?
Thanks
The formal explanation, as you probably already understand, is that the two functions have different signatures, (as Andrew Tobilko has already pointed out,) so one may not override the other.
So, I presume that by asking this question, what you really mean to ask is "why is this so", or "why can't the compiler figure things out so as to allow me to do this".
So, the practical explanation is as follows:
This is because you may have some method somewhere, (of some unrelated class even) which accepts a SuperClass as a parameter, and attempts to invoke its method1() like this:
public void someMethod( SuperClass s )
{
s.method1( 7 );
}
When the compiler finds this, it will pass 7 as a parameter to method1(), it will not pass a wrapped 7. However, s may not really be an instance of SuperClass, it may be an instance of SubClass, like this:
/* some code somewhere */
someMethod( new SubClass() );
This is valid because there is a principle in OOP known as the Liskov Substitution Principle which says that
if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.).
This is the same principle which allows you to assign a new ArrayList<String>() to a variable of type List<String>, and without it, nothing would work in OOP.
So, the compiler would have to pass a plain primitive 7, but the receiving method of SubClass would be expecting a wrapped 7, and that would not work. So, the language stipulates that an implicit conversion of this kind is invalid, to ensure that nonsensical situations of this kind may not arise.
Amendment
You might ask, "why would it not work?" The answer is that primitives in java correspond to machine data types of the underlying hardware, while wrapped primitives are objects. So, on the machine stack, a 7 would be represented by a machine word with the value of 7, while a wrapped 7 would be represented by something like 0x46d7c8fe, which would be a pointer to an object which contains the wrapped 7.
It is just because you have added #Override syntax above the method public void method1(Integer val) which asks compiler to look for the method with same signature in the base class which it can not find, thus it gives compile time error stating The method method1(Integer) of type SubClass must override a superclass method.
To understand the problem you asked
Integer is essentially wrapper of int then why such a declaration is prohibited?
Just because in any class you can declare method with these signatures
class SuperClass
{
public void method1(int val){
System.out.println("This is method in SuperClass.Val is:" + val);
}
public void method1(Integer val){
System.out.println("This is method in SuperClass.Val is:" + val);
}
}
The above declaration is valid and therefore the #override will try to match the method with exact signature in base class.
Why should it matter that Integer is a wrapper for int? They are still different types. You can have to methods with same name, that just differ by one having a primitive type and the other the matching wrapped type.
The only relation between int and Integer is: the compiler insert a conversion from one to the other when in any situation one is given but the other required.
So when you have a reference to the super type and call method1 the compiler generates byte code that essentially passes a primitive. If at runtime you use an actual class of SubClass, the overridden method won't match that call.
Of course the compiler could be written in a style that allows for this kind of overrides, but it would cause other problems:
If the decision was made at runtime, calling overriden methods would become expensive to to boxing/unboxing and checks, they might even trigger NullPointerExceptions
If done by converting everything to the boxed type at compile time, such calls would trigger constant boxing unboxing.
Also this would forbid constructs where you have a method that is overloaded to either take a type parameter or a primitive, because the type parameter might end up being the boxed type for that primitive, resulting to both methods being the same.
Integer and int are different.You could change Integer to int,or remove the "#Override"
I have two pieces of code. One works, another doesn't, but both seem to do identical things. This works:
short s=7;
but the below code doesn't. Instead, it gives error:
can't assign int to short
I know an integer number literal by default is int, but if it can be assigned directly above, then why not when passing to a method?
class Demo1{
public static void main(String[] args){
new Demo1().go(7);
}
void go(short s){System.out.println("short");}
}
The rules are different for assignment and for method overload resolution :
For assignment the JLS says :
In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
For overload resolution, JLS 15.12.2.2. says:
The method m is applicable by subtyping if and only if both of the
following conditions hold:
For 1 ≤ i ≤ n, either:
* Ai <: Si (§4.10), or
* Ai is convertible to some type Ci by unchecked conversion (§5.1.9), and Ci <: Si.
Here Ai is the type of the parameter passed to the method (int in your case, since 7 is an int literal). Si is the type of the method's formal parameters (short in your case). Ai <: Si means that Ai is a sub-type of Si. int is not a sub-type of short (the opposite is true), which is why the compiler doesn't accept new Demo1().go(7);.
First one Variable initialization.
Second one is passing parameter to method.
In Method you must pass exact data type of variable.
The language allows the narrowing conversion of constant int expression to short type, in Assignment Context. The same is not true for method invocation context. Relevant JLS section is JLS §5.2 and JLS § 5.3.
For assignment context, I'll list down the exact statement:
In addition, if the expression is a constant expression (§15.28) of
type byte, short, char, or int:
A narrowing primitive conversion may be used if the type of the
variable is byte, short, or char, and the value of the constant
expression is representable in the type of the variable.
While there is no such rule for invocation context conversion. That is why, you've to tell the compiler explicitly to do the conversion, by type-casting: new Demo1().go((short)7);
Now, as for why such a behaviour, the reason we come up with would just be a guess (real answer, language designer only knows).
You must cast the integer to short as follows:
short s = (short) 7;
This should work.
Your code should look like this:
class Demo1{
public static void main(String[] args){
new Demo1().go((short) 7);// change here.
}
void go(short s){System.out.println("short");}
}
The reason for implicit casting during a direct assignment and it not being present when passing a value during a method call is due to the difference between the assignment context and the invocation context which are self explained by the situation you are in. It is just how the java programming language works.
I see it as a positive factor as it helps the compiler decide which method your are calling if some of your methods have the same name but have the different data types in the paramaters. With you casting the given value to the data type in the paramater, the compiler will be able to distinguish which method you are calling, if implicit casting were to occur in the parameter how would the compiler know which data type to cast it to, if you had two methods with the same name but different parameters, say int and short.
In an assignment it is clear for the compiler to which argument to cast:
short s=7; //here is no doubt that it must be cast to short
In a method call, which could be possibly virtual, it is not decidable.
go(7); //here it could be anything
The compiler tries to find a signature which is type compatible, i.e.
void go(Integer i); // via auto boxing
void go(Number i); // autoboxing and supertype
It does not try casting, i.e. following would not work:
void go(short s); // short is neither a super type nor a subtype of int
void go(byte b); // byte is neither a super type nor a subtype of int
I rather do not expect go(new Integer(7)) to call void go(Short s) and that is the same with every types that are in no type hierarchy relation.
Here's a code example that doesn't compile:
public class Test {
public static void main(String[] args) {
method(1);
}
public static void method(int... x) {
System.out.println("varargs");
}
public static void method(Integer... x) {
System.out.println("single");
}
}
Can someone tell me the reason why these methods are ambiguous ? Thank you in advance.
There are 3 phases used in overload resolution (JLS 15.2.2):
The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
In your example, both methods are variable arity methods, so the third phase applies.
Now, since we have two methods to choose from, we look for the more specific method.
JLS 15.12.2.5. Choosing the Most Specific Method says :
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.
...
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:
...
m2 is not generic, m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ k). Additionally, if m2 has k+1 parameters, then the k+1'th variable arity parameter type of m1 is a subtype of the k+1'th variable arity parameter type of m2.
In your case you have two non-generic methods which are applicable by variable arity invocation (i.e. both have varargs). In order for one of the methods to be chosen when you call method(1), one of them has to be more specific than the other. In your case, each method only has one parameter, and for one of them to be more specific than the other, the type of that one parameter must be a subtype of the other method's parameter.
Since int is not a sub-type of Integer and Integer is not a sub-type of int, neither of your methods is more specific than the other. Hence the The method method(int[]) is ambiguous for the type Test error.
An example that would work :
public static void method(Object... x) {
System.out.println("varargs");
}
public static void method(Integer... x) {
System.out.println("single");
}
Since Integer is a sub-type of Object, the second method would be chosen when you call method(1).
Consider the method signatures
public static void foo(int a)
and
public static void foo(Integer a)
Before boxing and unboxing, the call foo(1) would not have been ambiguous. To ensure compatibility with earlier versions of Java, the call remains unambiguous. Therefore the first phase of overload resolution does not allow for boxing, unboxing, or variable arity invocation, which were all introduced at the same time. Variable arity invocation is when you call a varargs method by passing a sequence of parameters for the last argument (rather than an array).
However the resolution of method(1) for your method signatures allows for boxing and unboxing because both methods require a variable arity invocation. Since boxing is allowed, both signatures apply. Normally when two overloadings apply, the most specific overloading is chosen. However neither of your signatures is more specific than the other (because neither int nor Integer is a subtype of the other). Therefore the call method(1) is ambiguous.
You can make this compile by passing new int[]{1} instead.
Because they are ambiguous. According to JLS you can either do widening, boxing or boxing-then-widening. In your example there are 2 methods parameters which can be boxed/unboxed to each other. On compile time though it's not visible because of varargs, which were always not absolutely clear in java.
Even Sun recommended developers not to overload varargs methods, there were bugs in compiler related to it (see here).
The difference between int and Integer is that Integer is an object type.you can use in situation like finding the maximum number of type int , or comparing to integers
Integer object is already associated with methods like compare method:
public static void method(int x, int y) {
System.out.println(Integer.compare(x, y));
}
Find more at : http://docs.oracle.com/javase/7/docs/api/