Using default toString
The root Java class Object
has the toString
method to get the string representation of an object. If you'd like to get it, override this method in your class.
First, let's consider an example based on the default toString
implementation provided by the Object
class.
See the following example with the Account
class. It has three fields and one constructor.
class Account {
private long id;
private String code;
private Long balance;
public Account(long id, String code, Long balance) {
this.id = id;
this.code = code;
this.balance = balance;
}
// getters and setters
}
Let's create an instance of the class and get the string representation of the instance:
Account account = new Account(1121, "111-123", 400_000L);
String accString = account.toString(); // org.demo.example.Account@27082746
A string like
org.demo.example.Account@27082746
is not we need. This result is based on the full class name and its hashcode. It's the default behavior of the toString method
.Overriding toString when declaring a class
To include fields in the string representation of an object, we should override the standard behavior of the
toString
method.Here is another version of the
Account
class.class Account {
private long id;
private String code;
private Long balance;
public Account(long id, String code, Long balance) {
this.id = id;
this.code = code;
this.balance = balance;
}
// getters and setters
@Override
public String toString() {
return "Account{id=" + id + ",code=" + code + ",balance=" + balance + "}";
}
}
Let's create an instance of the class and get the string representation of the instance:
Account account = new Account(1121, "111-123", 400_000L);
String accString = account.toString(); // Account{id=1121,code=111-123,balance=400000}
The string representation is very useful for debugging and logging. You can use the method to display a string representation of an object in the standard input:
System.out.println(account.toString()); // or System.out.println(account) for short
Some modern IDEs, such as IntelliJ IDEA, allows generating the overridden
toString
method automatically. It's very convenient if your class has a lot of fields.Overriding toString when subclassing
If you have a class hierarchy you also can override
toString
.Here is a hierarchy of two classes:
Person
with the single string fieldname
;Employee
that extendsPerson
and adds the fieldsalary
.
class Person {
protected String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{name=" + name + "}";
}
}
class Employee extends Person {
protected long salary;
public Employee(String name, long salary) {
super(name);
this.salary = salary;
}
@Override
public String toString() {
return "Employee{name='" + name + ",salary=" + salary + "}";
}
}
It's a good practice to include the class name in the string representation when working with hierarchies.
Let's create two objects and prints them as strings.
Person person = new Person("Helena");
Employee employee = new Employee("Michael", 10_000);
System.out.println(person); // Person{name=Helena}
System.out.println(employee); // Employee{name='Michael,salary=10000}
So, we can override the
toString
method for classes in a hierarchy.Possible problems when overriding toString
Overriding the
toString
method looks very simple. But what if your class has another class as a type of a field? Sometimes it may cause an error.See the following example with
Person
and Passport
classes. We do not post getters and setters to make the code more compact.class Person {
private String name;
private Passport passport;
// getters and setters
@Override
public String toString() {
return "Person{name='" + name + ",passport=" + passport + "}";
}
}
class Passport {
private String counrty;
private String number;
// getters and setters
@Override
public String toString() {
return "Passport{country=" + country + ",number=" + number + "}";
}
}
If a person has no passport (
null
), the string representation will contain the null
.Here is an example of two objects.
Passport passport = new Passport();
passport.setNumber("4343999");
passport.setCountry("Austria");
Person person = new Person();
person.setName("Michael");
person.setPassport(passport);
System.out.println(person);
This code prints:
Person{name=Michael,passport=null}
Person{name=Michael, passport=Passport{country=Austria, number=4343999}}
It works very well, no any problems here! But what if the passport has the backward reference to the person and tries to get the string representation of the person?
Let's add the following field to the class
Passport
and the corresponding setter:private Person owner;
And modify the toString method as the follows:
@Override
public String toString() {
return "Passport{country=" + country + ",number=" + number + ",owner=" + owner + "}";
}
When we create two objects let's set the owner to the passport:
passport.setOwner(person);
Now we get the big problem - the program tries to get the string representation of the person that includes the string representation of passport that includes the string representation of the person. It causes
java.lang.StackOverflowError
.There are several ways to fix the arisen situation:
- do not include fields represented by your classes in the
toString
method; - exclude the field in the
toString
method from one of the classes.
So, be careful when includes fields in the
toString
method. Consider references between classes. If you don't need any information, exclude it better. It will save you from fatal mistakes.