Strange Java null behavior in Method Overloading [duplicate] - java

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

Why difference in types of arguments in overridden method (one is primitive and other is wrapper) is not allowed? [duplicate]

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"

why constructors with array as parameter precede constructors with Object in parameter [java]? [duplicate]

This question already has answers here:
Which overload will get selected for null in Java?
(3 answers)
Closed 8 years ago.
I have this confusing code:
public class Confusing {
private Confusing(Object o){
System.out.println("Object");
}
private Confusing(double[]dArray){
System.out.println("double array");
}
public static void main(String[] args){
new Confusing(null);
}
}
When "compiled" and run the program displays "double array" why arrays precede Object? Is there any other constructor situation where such confusing behavior will occur?
A double[] is an Object too, and when choosing which constructor (or method), Java will choose the one with the most specific parameter type that still matches. Because null will match any parameter type, the most specific type matches, and that is double[] here.
It's not confusing once you know this rule, but this will occur when two or more overloaded constructors (or two or more overloaded methods) differ only in that one of the parameter types in one of them is a subclass of the corresponding parameter in another.
The JLS, Section 15.12.2.5 states how Java chooses the method/constructor to invoke when multiple overloaded methods/constructors match:
The Java programming language uses the rule that the most specific method is chosen.
and
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.
Read more in Java Language Specification for choosing the Most specific method rules.
§8.4.9. Overloading
§13.4.23. Method and Constructor Overloading
§15.12.2.5. Choosing the Most Specific Method

reason the output with null in view [duplicate]

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.

Using null in overloaded methods in Java [duplicate]

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

Understanding which constructor is chosen and why

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.

Categories

Resources