Understanding which constructor is chosen and why - java

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.

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"

Which constructor is called first while passing null in the class having overloaded constructor?

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.

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.

Strange Java null behavior in Method Overloading [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.

variable argument in java

public class Demo {
public static String doit(int x,int y)
{
return"a";
}
public static String doit(int ...val)
{
return "b";
}
public static void main(String args[])
{
System.out.println(doit(4,5));
}
}
I have a doubt that why compilier is not showing any error since doit(4,5) is causing ambiguity
When I ru the code ,I get output as a ad not b why?
The Java Language Specification defines that first method ("a") should be called (rather than "b").
See http://docs.oracle.com/javase/specs/jls/se5.0/html/expressions.html#15.12.2
In order to maintain backwards compatibility with previous Java versions (before varargs was introduced), the compiler will always pick a method with the exact number of arguments, even if a varargs method also exists.
As to whether you get a warning or not, compilers are free to add additional warnings, and there may be some that do warn about this situation, I guess yours doesn't (at least not with the settings you have)
The JLS specifies the rules that are used to resolve ambiguity. The simplified version is that the compiler first attempts to match the call against the available overloads treating the varadic argument as a simple array. If that succeeds, that is it. If it fails, it tries again treating the last argument as varadic.
In this case, the first round of matching gives a definite match.
If you are interested, the JLS rules for determining what method should be used are given in Section 15.12.2. (Warning: actual JLS rules are significantly more involved than the simplified version above, and the language / notation used is very technical.)
public static String doit(int ...val)
{
return "b";
}
will be treated by compiler as
public static String doit(int[] val)
{
return "b";
}
when passing doit(2,2), 1st method will be called, as the arguments are not an array.
When passing doit(2,2,2), the arguments will converted to array and passed to 2nd method.
change the 1st method to
public static String doit(int x,int ...y)
{
return"a";
}
call doid(2,2), it will say error
doit(int, int[]) is ambigious.
Compiler always attempts to resolve the call to the most specific function call it can find which in this case is method A. This is not really a bug but if you consider it as such it's not a compiler bug, it's in the specs. You should see the crazy stuff you could get out of this once autoboxing comes into play.
The compiler tries to match the most specific alternative. It can however be argued that it should cause an ambiguity error.
From the Java Language Specification
15.12.2.5
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.
This is a compromise they made when doing the varargs spec (it's hard to know which gets called). It is recommended not to overload varargs method for this reason. Quote from their site:
Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called.

Categories

Resources