why String (String) constructor with null value cause compile-time error? I think there is 2+ constructor that takes Object and when init. it with null it doesn't know which to start. I'm wonder if there is another reason
String s = new String(null); // compile time error
StringBuffer sb = new StringBuffer(null); // no error
Normally, when you call a constructor or method for which multiple overridden versions might apply, Java will choose the most specific constructor or method. Section 15.12.2 of the Java Language Specification explains this in detail.
Suppose you have two overloaded methods, like this:
public void method(Object o) {
// ...
}
public void method(String s) {
// ...
}
When you call method(null), both these methods apply. Java chooses the most specific one, which is in this case the second method, that takes a String - because String is a more specific type than Object.
However, sometimes the most specific constructor or method cannot be determined. If we look at the constructors of class String that take one argument:
String(byte[] bytes)
String(char[] value)
String(String original)
String(StringBuffer buffer)
String(StringBuilder builder)
Note that there is no hierarchy between the types byte[], char[], String, StringBuffer and StringBuilder, so it's not possible to say that one of these constructors is more specific than the others. So, the Java compiler doesn't know which constructor to choose and will give you an error.
Because, compiler couldn't figure out which constructor to call. See here that how many one-argument-constructor it has.
[Edited]
You said, if there is another reason. So why not try out yourself. Do something like this,
byte[] b = null;
String s = new String(b); // complier should be fine with this
char[] c = null;
String s = new String(c); // complier should be fine with this
.... // you can try other constructors using similar approach.
Just to add more clarity to the solution:
Take this example:
public class Test {
public Test(Object obj) {
System.out.println("Object");
}
public Test(String obj) {
System.out.println("String");
}
public static void main(String[] args) {
new Test(null);
}
}
It will not throw any compile time error as compiler will choose the most specific constructor of available same types. However, if I change the class to:
public class Test {
public Test(Long lg){
System.out.println("Long");
}
public Test(Object obj) {
System.out.println("Object");
}
public Test(String obj) {
System.out.println("String");
}
public static void main(String[] args) {
new Test(null);
}
}
It will throw compile time error. As two separate constructor hierarchies are involved here and compiler has no way to make a decision.
One hierarchy is: Object-->String and second is Object-->Long so no way to choose between Long or String type constructors.
String has many constructors. null is available for them.
Edit: String has 5 one-argument-constructor, in Java 6. Thanks BoltClock and Adeel Ansari!
Related
Please tell me if I have the proper understanding of the following code:
public class Test {
public static void main(String[] args) {
A a = new A();
a.print();
}
}
class A {
String s;
A(String s) {
this.s = s;
}
void print() {
System.out.println(s);
}
}
The line “A a = new A();” invokes the class/constructor to create a new object with reference variable “a”. Class A has a defined constructor that requires a string argument, thus it does not have the default constructor. This means that the instantiation without any string arguments causes a compiler error.
If I were to add a string argument into the instantiation, e.g. A a = new A("goldfish"); the program would compile and run.
I am not sure if I have used the right vocabulary for this, so feel free to correct anything that is inaccurate/confusing. Thanks!
Your understanding is pretty much correct. The one thing that I would change is "create a new object" to "create a new instance of A" with a reference to a java.lang.String in parameter s. In this case the constructor assigns that parameter to a field, but it can do something else with it entirely (such as use it to calculate a different value for some field).
What you wrote is roughly correct.
To be more precise: "invokes the class/constructor" is not entirely correct. A a = new A(); intends to invoke the constructor (invoking a class doesn't mean anything).
Though constructors are not methods, you can think of them a bit like methods: if your class has defined a method like so :
public void myMethod(String s) { ... }
Then trying to call myMethod() without any argument would fail. It's the same here.
Consider the following example code:
public class TestClass {
public void doSth(String str, String l, Object... objects) {
System.out.println("A");
}
public void doSth(String str, Object... objects) {
System.out.println("B");
}
}
When I now call new TestClass().doSth("foo", "bar") I get the expected result A. But if I change the method signature of the first method by chaging the parameter l to a primitive type:
public class TestClass {
public void doSth(String str, long l, Object... objects) {
System.out.println("A");
}
public void doSth(String str, Object... objects) {
System.out.println("B");
}
}
calling new TestClass().doSth("foo", 2L) will yield a reference to call ambiguous compile time error.
I thought about that one for some time now and also consulted this stackoverflow question, but I was unable to understand why this happens. In my opinion the doSth("foo", 2L) is more specific to the doSth(String string, long l, Object... obj) signature and should allow the compiler to also come to this conclusion.
In this case, auto-boxing is causing you grief. Ironically, before that you're correct - the "long" version would have easily been picked.
Basically the compiler knows that it can create a Long from your value which, of course, is an Object. So it is still confused as either the long or the Long version could be used. Is one "better" than the other? Maybe but it is a pretty fine line.
At this state, I can only report my observation, not the exact argumentation as to WHY Java behaves, like it does.
First, changing the methods to
void doSth(long l) {...}
void doSth(Object o) {...}
gets rid of the problem, i.e. doSth(2L); will yield the expected result.
Going one step further, changing the method parameter to varargs
void doSth(long... ls) {...}
void doSth(Object... os) {...}
together with the call doSth(2l); yields the same compilation error as reported by OP.
My suggestion at this stage is that encapusalting the parameter into an array, together with Autoboxing causes the havoc. My knowledge about the JLS is not firm enough to explain this properly.
I came across this question in a quiz,
public class MoneyCalc {
public void method(Object o) {
System.out.println("Object Verion");
}
public void method(String s) {
System.out.println("String Version");
}
public static void main(String args[]) {
MoneyCalc question = new MoneyCalc();
question.method(null);
}
}
The output of this program is "String Version". But I was not able to understand why passing a null to an overloaded method chose the string version. Is null a String variable pointing to nothing ?
However when the code is changed to,
public class MoneyCalc {
public void method(StringBuffer sb) {
System.out.println("StringBuffer Verion");
}
public void method(String s) {
System.out.println("String Version");
}
public static void main(String args[]) {
MoneyCalc question = new MoneyCalc();
question.method(null);
}
}
it gives a compile error saying "The method method(StringBuffer) is ambiguous for the type MoneyCalc"
Is null a String variable pointing to nothing ?
A null reference can be converted to an expression of any class type. So in the case of String, this is fine:
String x = null;
The String overload here is chosen because the Java compiler picks the most specific overload, as per section 15.12.2.5 of the JLS. In particular:
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 second case, both methods are still applicable, but neither String nor StringBuffer is more specific than the other, therefore neither method is more specific than the other, hence the compiler error.
Additionally, the JLS 3.10.7 also declares that "null" is a literal value of the "null type". Therefore there exists a type called "null".
Later, the JLS 4.1 states that there exists a null type of which is impossible to declare variables, but you can use it through the null literal only. Later it says:
The null reference can always undergo a widening reference conversion
to any reference type.
Why the compiler chooses to widen it to String might well be explained in Jon's answer.
You can assign a string to a null value so it is valid and the order for java and most programming languages is fit to the closest type and then to object.
To answer the question in the title: null is neither a String nor an Object, but a reference to either can be assigned to null.
I'm actually surprised this code even compiles. I tried something similar previously and I got a compiler error saying that the call was ambiguous.
However, in this case, it seems like the compiler is choosing the method which is lowest on the food chain. It's assuming that you want the least generic version of the method in order to help you out.
I'll have to see if I can dig up the example where I got a compiler error in this (seemingly) exact same scenario, though...]
EDIT: I see. In the version I made, I had two overloaded methods accepting a String and an Integer. In this scenario, there is no "most specific" parameter (as in Object and String), so it can't choose between them, unlike in your code.
Very cool question!
As String type is more specific than Object type. Let's say you add one more method that takes an Integer type.
public void method(Integer i) {
System.out.println("Integer Version");
}
Then you will get a compiler error saying that the call is ambiguous. As now we two equally specific methods with same precedence.
Java compiler gives most derived class type to assign null.
Here is the example to understand it :
class A{
public void methodA(){
System.out.println("Hello methodA");
}
}
class B extends A{
public void methodB(){
System.out.println("Hello methodB");
}
}
class C{
public void methodC(){
System.out.println("Hello methodC");
}
}
public class MyTest {
public static void fun(B Obj){
System.out.println("B Class.");
}
public static void fun(A Obj){
System.out.println("A Class.");
}
public static void main(String[] args) {
fun(null);
}
}
output: B Class.
on the other hand:
public class MyTest {
public static void fun(C Obj){
System.out.println("B Class.");
}
public static void fun(A Obj){
System.out.println("A Class.");
}
public static void main(String[] args) {
fun(null);
}
}
Result : The method fun(C) is ambiguous for the type MyTest
Hope it will help to understand this case better.
Source: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Concept: Most specific method
Explanation: 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. Try casting the null on to specific type and the method you wish will be automatically called.
I would say neither. NULL is a state not a value. Check out this link for more info on this (the article applies to SQL, but I think it helps with your question as well).
I have two classes defined like this:
class A {
public static String getName(){
Class c = getCalledClass();
return c.getSimpleName();
}
}
class B extends A {
//no methods are defined here!
}
I want to know if it is possible to compose the static method getCalledClass() such that calling A.getName() will return A and B.getName() will return B?
Thanks.
This is not possible, at least not in the general sense that you've asked.
There is no method B.getName(). While you can type that in code, it will be compiled to identical bytecode to A.getName() (and I think you get a compiler warning too).
Thus at runtime, there is no way to tell how someone referenced the static method - just as there's no way to tell what local variable names a caller is using.
I don't know if you can remove static or not as per your requirement. If you can remove static and use polymorphism then you can get what you want. Below is the code example I tested.
class A {
public String getName(){
Class c = this.getCalledClass();
return c.getSimpleName();
}
Class getCalledClass() {
return A.class;
}
}
class B extends A {
Class getCalledClass() {
return B.class;
}
}
class TestApplication {
public static void main(String[] args) {
A objA = new A();
System.out.println(objA.getName());
A objB = new B();
System.out.println(objB.getName());
}
}
When the javac compiler finds a call to B.getName(), it resolves it there-and-then to A.getName() and puts a static call to A.getName() in the byte code.
There does not exist any mechanism in java which can deduce the form used in the source code from the byte code.
If you want B.getName() to be a different method to A.getName(), you have to define a method called getName() in B. Since in that method the called class will always be 'B', there's no need to mess around with the stack trace or other mechanisms. However, generally if you think it matters what was before the dot, there's probably a better object oriented design for you to use instead.
The stack trace will contain only the name of the class that that static method is defined on. Even if you call B.method(), you'll see A.method() in the stack trace. Using the static scheme you cannot extract reliably the info you want.
If you use non-static methods then this will be the instance of the type you are looking for.
public String getName() {
return this.class.getSimpleName();
}
I came across this question in a quiz,
public class MoneyCalc {
public void method(Object o) {
System.out.println("Object Verion");
}
public void method(String s) {
System.out.println("String Version");
}
public static void main(String args[]) {
MoneyCalc question = new MoneyCalc();
question.method(null);
}
}
The output of this program is "String Version". But I was not able to understand why passing a null to an overloaded method chose the string version. Is null a String variable pointing to nothing ?
However when the code is changed to,
public class MoneyCalc {
public void method(StringBuffer sb) {
System.out.println("StringBuffer Verion");
}
public void method(String s) {
System.out.println("String Version");
}
public static void main(String args[]) {
MoneyCalc question = new MoneyCalc();
question.method(null);
}
}
it gives a compile error saying "The method method(StringBuffer) is ambiguous for the type MoneyCalc"
Is null a String variable pointing to nothing ?
A null reference can be converted to an expression of any class type. So in the case of String, this is fine:
String x = null;
The String overload here is chosen because the Java compiler picks the most specific overload, as per section 15.12.2.5 of the JLS. In particular:
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 second case, both methods are still applicable, but neither String nor StringBuffer is more specific than the other, therefore neither method is more specific than the other, hence the compiler error.
Additionally, the JLS 3.10.7 also declares that "null" is a literal value of the "null type". Therefore there exists a type called "null".
Later, the JLS 4.1 states that there exists a null type of which is impossible to declare variables, but you can use it through the null literal only. Later it says:
The null reference can always undergo a widening reference conversion
to any reference type.
Why the compiler chooses to widen it to String might well be explained in Jon's answer.
You can assign a string to a null value so it is valid and the order for java and most programming languages is fit to the closest type and then to object.
To answer the question in the title: null is neither a String nor an Object, but a reference to either can be assigned to null.
I'm actually surprised this code even compiles. I tried something similar previously and I got a compiler error saying that the call was ambiguous.
However, in this case, it seems like the compiler is choosing the method which is lowest on the food chain. It's assuming that you want the least generic version of the method in order to help you out.
I'll have to see if I can dig up the example where I got a compiler error in this (seemingly) exact same scenario, though...]
EDIT: I see. In the version I made, I had two overloaded methods accepting a String and an Integer. In this scenario, there is no "most specific" parameter (as in Object and String), so it can't choose between them, unlike in your code.
Very cool question!
As String type is more specific than Object type. Let's say you add one more method that takes an Integer type.
public void method(Integer i) {
System.out.println("Integer Version");
}
Then you will get a compiler error saying that the call is ambiguous. As now we two equally specific methods with same precedence.
Java compiler gives most derived class type to assign null.
Here is the example to understand it :
class A{
public void methodA(){
System.out.println("Hello methodA");
}
}
class B extends A{
public void methodB(){
System.out.println("Hello methodB");
}
}
class C{
public void methodC(){
System.out.println("Hello methodC");
}
}
public class MyTest {
public static void fun(B Obj){
System.out.println("B Class.");
}
public static void fun(A Obj){
System.out.println("A Class.");
}
public static void main(String[] args) {
fun(null);
}
}
output: B Class.
on the other hand:
public class MyTest {
public static void fun(C Obj){
System.out.println("B Class.");
}
public static void fun(A Obj){
System.out.println("A Class.");
}
public static void main(String[] args) {
fun(null);
}
}
Result : The method fun(C) is ambiguous for the type MyTest
Hope it will help to understand this case better.
Source: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Concept: Most specific method
Explanation: 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. Try casting the null on to specific type and the method you wish will be automatically called.
I would say neither. NULL is a state not a value. Check out this link for more info on this (the article applies to SQL, but I think it helps with your question as well).