In this article to we will see how to override the toString method and what are the best practices in Java. Java provides a default implementation for the method toString through a class java.lang.Object which is inherited by all Java classes . However, what it returns is not very informative. Your returned value is composed of the class name followed by an at sign (@) and the representation of the unsigned hash code in hexadecimal eg, zip @ 163b91, where zip would be the class name followed by the (@) sign and the hash code . Below is the pattern followed by toString in class Object:
public String toString () { return getClass (). getName () + "@" + Integer.toHexString (hashCode ()); }
The method toString should return a concise , yet informative representation which is easy for a person to read. We agree that zip @ 163b91 is not informative. Another important detail is that the contract of toString recommends that sub classes must overlap. Providing a good implementation of toString makes the class more enjoyable to use. The method toString is always called automatically when an object is passed to println, printf, to the concatenation operator for strings or assert, or displayed by a debugger.
Benefits to Override toString
The benefits of providing a good toString method is, it extends to the objects containing references to these instances, especially collections. For example, it is better to see the display mapping: “Jenny = zip @ th163b91” than seeing this “Jenny = 92110300”?
The method toString should return all the interesting information contained in the object. As in the above example of zip code, when we have a big ideal object it creates as “Manisha lives in , Pune in the state of Maharashtra with zip 92110300.” Ideally, the string is always self-explanatory. As an introductory example to use the toString method we will create an Address class as below:
Listing 1 : Example of the Address class without overriding the toString.
package net.javabeat; public class Address { private Integer zip; private String city; private String state; private String street; private Integer number; public Integer getZip() { return zip; } public void setZip(Integer zip) { this.zip = zip; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } }
Now create another class Main which is responsible for displaying the data from the user’s address previously defined.
Listing 2 : Example display the Address class.
package net.javabeat; public class Main { public static void main(String args[]) { Address address = new Address(); address.setNumber(92110300); address.setCity("Pune"); address.setStreet("M.G.Road"); address.setZip(411027); address.setState("Maharashtra"); System.out.println(address); } }
The output of on execution of the above class is the text below:
net.javabeat.Address@49c68e73
We note that it does not report anything at all for those who are accessing the toString method of that class. Now let’s imagine the following scenario where we have a collection of Address objects.
Listing 3 : Example of calling the toString method of the Address class in a collection.
package net.javabeat; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String args[]) { List<Address> addresses = new ArrayList<Address>(); // Adds First Address Address address = new Address(); address.setNumber(92110300); address.setCity("Bangalore"); address.setStreet("Mysore.Road"); address.setZip(590006); address.setState("Karnataka"); addresses.add(address); // Adds Second Address address = new Address(); address.setNumber(92110301); address.setCity("Pune"); address.setStreet("M.G.Road"); address.setZip(411027); address.setState("Maharashtra"); addresses.add(address); // Adds Third Address address = new Address(); address.setNumber(92110302); address.setCity("Pangim"); address.setStreet("Vasco Road"); address.setZip(411026); address.setState("Goa"); addresses.add(address); System.out.println(addresses); } }
We have as a result of the list printed as below:
[net.javabeat.Address@69af0fcf, net.javabeat.Address@6437153d, net.javabeat.Address@5c538b31]
However, now we if override the toString method to improve the understanding of our class. Change the Address class as below:
Listing 4 : Example of the Address class overriding the toString method.
package net.javabeat; public class Address { private Integer zip; private String city; private String state; private String street; private Integer number; public Integer getZip() { return zip; } public void setZip(Integer zip) { this.zip = zip; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } @Override public String toString() { return "Street: " + this.street + ", Number: " + this.number + ", City: " + this.city + ", State: " + this.state + ", zip: " + this.zip; } }
Now output is as below:
[Street: Mysore.Road, Number: 92110300, City: Bangalore, State: Karnataka, zip: 590006, Street: M.G.Road, Number: 92110301, City: Pune, State: Maharashtra, zip: 411027, Street: Vasco Road, Number: 92110302, City: Pangim, State: Goa, zip: 411026]
We have a result much more clear and informative than the previous result without even iterating the whole list . As a side note, we could also use a StringBuffer (Read : StringBuilder) to concatenate our Strings, but the return should be stringbuffer.toString (). The example below demonstrates the use of StringBuffer:
Listing 5 : Example of the Address class overriding the toString method using StringBuffer.
package net.javabeat; public class Address { private Integer zip; private String city; private String state; private String street; private Integer number; public Integer getZip() { return zip; } public void setZip(Integer zip) { this.zip = zip; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } @Override public String toString() { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("Address:"); stringBuffer.append(this.street); stringBuffer.append("Number:"); stringBuffer.append(this.number); stringBuffer.append("City:"); stringBuffer.append(this.city); stringBuffer.append("State:"); stringBuffer.append(this.state); stringBuffer.append("Postal Code:"); stringBuffer.append(this.zip); return stringBuffer.toString(); } }
Another important point is that we always provide programmatic access to all information contained in the value returned by toString. For example, imagine a class phone, in this case the class should contain getters for the area code, prefix, and line number. If this is not done then the programmer would be forcing other programmers who need this information to analyze the string. This will be an unneccessary work for the developers, hence the new developer have to format the code which would cost the application.
Summary
In this article we studied what is toString, how can we can override this method, its advantages to define it and what are the best practices to override the method. More over, we studied some examples that demonstrate the use of toString and how we can benefit from this method to override.