Why does this Java code implicitly calls toString() method?
System.out
is a PrintStream
instance that is a static member of System
. The PrintStream
class has a function println()
that accepts an argument of type Object
. That function, in Open JDK, looks like the following:
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
And if you look at String.valueOf()
, which accepts an argument of type Object
, you can see:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
There is no magic. It's just a Java class that calls toString
on objects.
Further Reading
- Take a look at the docs for the PrintStream class: https://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html
Explicit vs implicit call of toString
There's little difference. Use the one that's shorter and works more often.
If you actually want to get the string value of an object for other reasons, and want it to be null friendly, do this:
String s = String.valueOf(obj);
Edit: The question was extended, so I'll extend my answer.
In both cases, they compile to something like the following:
System.out.println(new StringBuilder().append("obj: ").append(obj).toString());
When your toString()
is implicit, you'll see that in the second append.
If you look at the source code to java, you'll see that StringBuilder.append(Object)
looks like this:
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
where String.valueOf
looks like this:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
Now, if you toString()
yourself, you bypass a null check and a stack frame and go straight to this in StringBuilder
:
public StringBuilder append(String str) {
super.append(str);
return this;
}
So...very similar things happens in both cases. One just does a little more work.
How toString() is automatically call inside println()
Short answer: public frog1.toString()
method call is used inside System.out.println(Object x)
call stack.
But how? Let's find it out together :)
Take a look at the PrintStream
class (which instance is used as System.out
field value by default) source code and its println
implementation that accepts Object
argument:
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
And the String
's valueOf
method for argument with Object
type is:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
obj
is frog1
in your case, its toString()
method is called and returns "Hello"
String instance for the console output)
Java implicit call of toString() when using the this keyword
Because that's what happens when you use the String concatenation operator (+
).
It calls the toString()
method of the object being added (which is guanateed to be there since it's defined in Object
).
In your case, that's the current object (this
) so you get this.toString()
called. And because you've overriden toString()
in your class, you get the result of that.
Edit to add: Note that there's no difference between + this
and + this.toString()
; you'll end up with the same result.
Specifically, this is covered by section 15.18.1 of the JLS
If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run time.
And §5.1.11 states:
...
Now only reference values need to be considered:
If the reference is null, it is converted to the string "null" (four
ASCII characters n, u, l, l).Otherwise, the conversion is performed as if by an invocation of the
toString method of the referenced object with no arguments; but if the
result of invoking the toString method is null, then the string "null"
is used instead.
Implicitly calling a method in a class from main
Java calls toString
when you use +
on a String
and an object.
So your
System.out.println("Inizio: " + inizio + "\n" + "Fine: " + fine);
is the same as
System.out.println("Inizio: " + inizio.toString() + "\n" + "Fine: " + fine.toString());
except that if inizio
or fine
is null
, the first won't give you an error (you'll see null
in the string instead) but the second will.
From the JLS's section on the String Concatenation operator:
If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run time.
which refers to the String Conversion section, which says (after discussing converting primitives to string):
...
Now only reference values need to be considered:
If the reference is null, it is converted to the string "null" (four ASCII characters n, u, l, l).
Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but if the result of invoking the toString method is null, then the string "null" is used instead.
Why can class objects call the toString method if the class does not provide an implementation for the method?
Every Java class is a subclass of Object class.
Thus, by inheritance, all the operations available in Object class are available in every Java class. This includes toString() operation :).
Related Topics
Java If VS. Try/Catch Overhead
How to Solve the "A Generic Array of T Is Created for a Varargs Parameter" Compiler Warning
Java Conditional Operator : Result Type
Can Hibernate Work with MySQL's "On Duplicate Key Update" Syntax
Data Structures That Can Map a Range of Keys to a Value
How to Convert a Binary String to a Base 10 Integer in Java
Java Sorting Based on Enum Constants
Pass Array to Oracle Procedure
Fixedthreadpool VS Cachedthreadpool: the Lesser of Two Evils
Exception Without Stack Trace in Java
What Is the Idiomatic Way to Compose a Url or Uri in Java
What Is Xml Bom and How to Detect It
How to Delete a Folder with Files Using Java
JSON and Java - Circular Reference
How to Use a Java8 Lambda to Sort a Stream in Reverse Order
In Java 8, Why Is the Default Capacity of Arraylist Now Zero