This question already has answers here:
Which overload will get selected for null in Java?
(3 answers)
Closed 9 years ago.
I have the following code snippet:
public static void foo(Object x) {
System.out.println("Obj");
}
public static void foo(String x) {
System.out.println("Str");
}
If I call foo(null) why is there no ambiguity? Why does the program call foo(String x) instead of foo(Object x)?
why the program calls foo(String x) instead of foo(Object x)
That is because String class extends from Object and hence is more specific to Object. So, compiler decides to invoke that method. Remember, Compiler always chooses the most specific method to invoke. See Section 15.12.5 of JLS
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.
However, if you have two methods with parameter - String, and Integer, then you would get ambiguity error for null, as compiler cannot decide which one is more specific, as they are non-covariant types.
The type of null is by definition a subtype of every other reference type. Quote JLS 4.1:
The null reference can always undergo a widening reference conversion to any reference type.
The resolution of the method signature involved in an invocation follows the principle of the most specific signature in the set of all compatible signatures. (JLS 15.12.2.5. Choosing the Most Specific Method).
Taken together this means that the String overload is chosen in your example.
It's calling the most specific method.
Since String is a subclass of Object, String is "more specific" than Object.
When given a choice between two methods where the argument is valid for both parameters, the compiler will always choose the most specific parameter as a match. In this case, null is a literal that can be handled as an Object and a String. String is more specific and a subclass of Object so the compiler uses it.
Related
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"
Below is the java class having 3 overloaded constructors :
public class Test {
public Test(Object i){
System.out.println("Object invoked");
}
public Test(String i){
System.out.println("String invoked");
}
public Test(int k){
System.out.println("Integer invoked");
}
public static void main(String[] args) throws Exception {
Test t = new Test(null);
}
}
If null value is passed while creating the new instance of class, which constructor will be invoked ? What is the reason ?
Java always chooses the most specific method (or constructor) that would be applicable for the argument you pass. In this case, that's the String constructor -- String is a subclass of Object.
Think about what would happen if you had
new Test("some string")
Both the Object and String constructors would be applicable here. After all, the argument is both an Object and a String. However, it is clear that the String constructor will be invoked because it is more specific than the Object constructor and still is applicable given the argument.
null is no different; these two constructors are still applicable, and the String constructor is still chosen over the Object one for the same reason.
Now, if there were two equally "specific" constructors present (e.g. if you had an Integer constructor) there would be a compilation error when you call Test(null).
This is outlined in more detail in JLS §15.12.2:
The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.
There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is one used at run time to perform the method dispatch.
The explicit process of determining which method is the most specific is outlined in JLS §15.12.2.5.
The answer is: Test(String) is invoked.
Why?
When determining which of a set of overloaded methods will be invoked, the Java compiler will attempt to match the most concrete type. And it will first attempt to match a signature before employing autoboxing. (#arshajii provided a perfect reference into the Java Language Spec on this)
Here, Object is the most abstract class in the type system. String subclasses from Object and is therefore more specific/concrete.
The logic behind this is that if you are overloading a method with a more specific-typed parameter, you're likely wanting to do more with that object (when you subclass, you typically add methods). If method signature determination worked the other way (i.e. the more abstractly-typed signature winning; here, Test(Object)), then none of the more concrete signatures would ever get called.
This question already has answers here:
Which overload will get selected for null in Java?
(3 answers)
Closed 9 years ago.
I have the following code snippet:
public static void foo(Object x) {
System.out.println("Obj");
}
public static void foo(String x) {
System.out.println("Str");
}
If I call foo(null) why is there no ambiguity? Why does the program call foo(String x) instead of foo(Object x)?
why the program calls foo(String x) instead of foo(Object x)
That is because String class extends from Object and hence is more specific to Object. So, compiler decides to invoke that method. Remember, Compiler always chooses the most specific method to invoke. See Section 15.12.5 of JLS
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.
However, if you have two methods with parameter - String, and Integer, then you would get ambiguity error for null, as compiler cannot decide which one is more specific, as they are non-covariant types.
The type of null is by definition a subtype of every other reference type. Quote JLS 4.1:
The null reference can always undergo a widening reference conversion to any reference type.
The resolution of the method signature involved in an invocation follows the principle of the most specific signature in the set of all compatible signatures. (JLS 15.12.2.5. Choosing the Most Specific Method).
Taken together this means that the String overload is chosen in your example.
It's calling the most specific method.
Since String is a subclass of Object, String is "more specific" than Object.
When given a choice between two methods where the argument is valid for both parameters, the compiler will always choose the most specific parameter as a match. In this case, null is a literal that can be handled as an Object and a String. String is more specific and a subclass of Object so the compiler uses it.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Method Overloading for NULL parameter
The following code compiles and goes fine.
public class Main
{
public void temp(Object o)
{
System.out.println("The method with the receiving parameter of type Object has been invoked.");
}
public void temp(String s)
{
System.out.println("The method with the receiving parameter of type String has been invoked.");
}
public void temp(int i)
{
System.out.println("The method with the receiving parameter of type int has been invoked.");
}
public static void main(String[] args)
{
Main main=new Main();
main.temp(null);
}
}
In this code, the method to be invoked is the one that accepts the parameter of type String
The docs say.
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.
but I don't understand when one of the methods in the code that accepts the parameter of the primitive int is modified to accept the parameter of the wrapper type Integer such as,
public void temp(Integer i)
{
System.out.println("The method with the receiving parameter of type Integer has been invoked.");
}
a compile-time error is issued.
reference to temp is ambiguous, both method temp(java.lang.String) in
methodoverloadingpkg.Main and method temp(java.lang.Integer) in
methodoverloadingpkg.Main match
In this particular scenario, why is it legal to overload a method with a primitive data type which however doesn't appear to be the case with its corresponding wrapper type?
If you were asked what is more specialized "String" or "Object", what would you say? Evidently "String", right?
If you were asked: what is more specialized "String" or "Integer"? There is no answer, they are both orthogonal specializations of an object, how can you choose between them? Then you must be explicit regarding which one you want. For instance by casting your null reference:
question.method((String)null)
When you use primitive types you do not have that problem because "null" is a reference type and cannot conflict with primitive types. But when you use reference types "null" could refer to either String or Integer (since null can be cast to any reference type).
See the answer in the other question that I posted in the comments above for further and deeper details and even a few quotes from the JLS.
Where you pass null as argument for an overloaded method, the method choosen is the method with the most specialized type, so in this case: String is choosen rather than the most tolerant: Object.
Among Object/String/int the choice is clear for the compiler:
you will get the String's one cause an int cannot be null and so its corresponding method is not eligible to be called in this case.
But if you change int for Integer, compiler will be confuse because both methods taking String is as accurate as Integer's one (orthogonal in hierarchy).
And the compiler can't (doesn't want? ^^) choose randomly.
First to understand why main.temp(null); resolves to temp(String s) and not temp(Object o) I point you at my old reply to Java method dispatch with null argument. Essentially, the literal null is of type nulltype and Object < String < nulltype. So null is closer to String then it is to Object.
Now, null is as close to String as it is to Integer, so when you add test(Integer) it becomes ambiguous
Why following program every time prints I'm string and not I'm object. or I'm int.?
public class Demo {
public Demo(String s){
System.out.println("I'm string");
}
public Demo(int i){
System.out.println("I'm int.");
}
public Demo(Object o){
System.out.println("I'm object.");
}
public static void main(String[] args) {
new Demo(null);
}
}
Also if I replace int with Integer. It gives error as The constructor Demo(String) is ambiguous. Why?
null can be converted to Object or String, but not int. Therefore the second constructor is out.
Between the conversion to Object or the conversion to String, the conversion to String is more specific, so that's what's picked.
The JLS section 15.12.2 describes method overload resolution, and I believe the same approach is used for constructor resolution. Section 15.12.2.5 describes choosing the most specific method (constructor in this case):
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.
This about the constructor invocation with Object or String arguments - any invocation handled by new Demo(String) could also be passed on to new Demo(Object) without a compile-time type error, but the reverse is not true, therefore the new Demo(String) one is more specific... and thus chosen by the overload resolution rules.
To answer your second question (since Jon Skeet has already covered the first), when you have both a String constructor and an Integer constructor the compiler doesn't know what you mean by null in new Demo(null): it could be either a String or an Integer.
Because String can't be cast to Integer (and vice versa) the compiler gives up and reports the ambiguous error. This is in contrast to the String vs Object choice when you don't have the Integer constructor.
When you have constructors or methods like the above, the compiler generally tries to find the closest match possible and uses that.
The way to think of null in these circumstances is it acts as a subtype of all types (yes, it's not strictly speaking true but it provides a good platform for thinking about what happens). So using this rule it fits string more closely than object, hence this is the constructor that is executed.
The fact the String constructor is mentioned in by the compiler in your second error:
The constructor Demo(String) is ambiguous.
is not significant. This is because the Constructor which takes a String is the first declared constructor, so the compiler uses it in its error message. Change to have the Object constructor first and you get:
The constructor Demo(Object) is ambiguous.
What it's trying to say is there's ambiguity between the constructor which takes the Integer and the Object, so you have to be more specific, as null can be applied to each. Integer IS-NOT-A String, so the two types are not compatible. You need to be more specific so the compiler can bind the constructor call.
See #Jon Skeet answer for why the compiler raises an error in some instances and not in others.