What is the toString Method in Java
Overview
1. Introduction
In this article, I'll explain the toString method and how to override it from the Object class in our Java classes.
2. The Default Behavior of toString
Any class in Java extends the Object class; thus, any class can override its methods. The toString method is one of these methods.
In the Object class, the default behavior of toString prints the object's memory address. That's not very practical since most applications need to serialize or print to output the object's value, not its address.
Whenever you print an instance of Object's sub-class, the JVM calls the default implementation that prints the object's memory address. If we want a different behavior, we need to override the toString method with the implementation we want.
Throughout this article, we'll use the Person class to exemplify the usage of toString:
1 class Person {
2 public String name;
3 public String lastName;
4
5 public Person(String name, String lastName) {
6 this.name = name;
7 this.lastName = lastName;
8 }
9 }
Let's also define a class with a main method to run our tests:
1public class ToString {
2
3 public static void main(String[] args) {
4 Person person = new Person("John", "Wick");
5
6 System.out.println(person);
7 }
8}
In the previous code, we define a Person object with name = John, and lastName = Wick. Let's check what the println method outputs with the person object as input.
1com.blog.codesnippets.strings.ToString$Person@77459877
The default toString implementation outputs the fully qualified name of the object followed by its memory address (represented as @77459877).
3. How to Override the toString method
Commonly, we are interested in serializing the object's values instead of its memory address. Suppose the application requires serializing a Person field in a key-value style. To achieve that, we can create a custom implementation of toString. The implementation varies for classes with only primitive fields and classes with nested classes.
3.1. Implementing toString Method for Classes with Primitive Types
That scenario is straightforward. We need only to override toString in the target class using our custom implementation. Let's see a modification of the Person class with toString:
1public class Person {
2
3 public String name;
4 public String lastName;
5
6 public Person(String name, String lastName) {
7 this.name = name;
8 this.lastName = lastName;
9 }
10
11 @Override
12 public String toString() {
13 return "Name: " + name + "\nLast name: " + lastName;
14 }
15}
The above code overrides the default behavior of toString and returns a formatted template of the name and lastName fields. If we call the same main method again, we should get the following output:
1Name: John
2Last name: Wick
Important note: The custom version of toString should follow the method overriding rules: the sub-class toString method should have the same signature as the super-class method. The @Override annotation helps notice an override, although it's optional.
3.2. toString for Objects with Nested Objects
Working with nested classes is a bit different than primitive types. To correctly serialize the base class, we must define a toString method in the nested class. Let's add a nested class to Person that represents its address:
1public class Address {
2
3 public String street;
4 public String city;
5
6 public Address(String street, String city) {
7 this.street = street;
8 his.city = city;
9 }
10}
The new Person class is as follows:
1public class Person {
2
3 public String name;
4 public String lastName;
5
6 public Address address;
7
8 public Person(String name, String lastName, Address address) {
9 this.name = name;
10 this.lastName = lastName;
11 this.address = address;
12 }
13
14 @Override
15 public String toString() {
16 return "Name: " + name + "\nLast name: " + lastName + "\nAddress:" + address;
17 }
18}
Let's test our implementation of toString, creating a Person with an Address nested object with the main method below:
1public class ToString {
2
3 public static void main(String[] args) {
4 Person person = new Person("John", "Wick", new Address("Fifth Avenue", "New York"));
5
6 System.out.println(person);
7 }
8}
Let's check the output for the main method above:
1Name: John
2Last name: Wick
3Address: com.blog.codesnippets.strings.Address@548c4f57
Since there's no custom implementation of toString in the Address class, the code outputs its memory address, as we see previously with Person. To avoid that, we also need to provide another implementation for Address's toString method. Let's change the content in the Address class to the following:
1public class Address {
2
3 public String street;
4 public String city;
5
6 public Address(String street, String city) {
7 this.street = street;
8 this.city = city;
9 }
10
11 @Override
12 public String toString() {
13 return "\n\tStreet: " + street + "\n\tCity: " + city;
14 }
15}
The serialization of Address to string should return the street and city in a key-value format. After running the same main method, we should get the following results:
1Name: John
2Last name: Wick
3Address:
4 Street: Fifth Avenue
5 City: New York
Now, the Person object and its nested objects are correctly serialized to string.
4. Generate toString Using Tools
Overriding the toString method for all classes in your application is tedious. Let's see ways to improve that process instead of manually doing it.
4.1. Using Lombok
One option is to use the @ToString annotation of Lombok. If you use a build system like Maven or Gradle, you can add the Lombok dependency to your project and put the @ToString above the class name. In our example, that annotation should be at both Person and Address classes.
Please look at their documentation on configuring the most recent version of it on your project.
4.2. Using IntelliJ IDEA
Using IntelliJ, we can generate the toString with the following steps:
- Press Alt + Insert in your class. Or, right-click on its name and choose Generate.
- Select the toString option.
- Choose the fields you want to serialize in the custom toString method and press OK.
This option can be helpful if your application doesn't use external tools like Lombok.
5. Conclusion
In this article, we've seen the purpose of the method toString, how to override it, and how to generate it using tools.