Método toString() em Java
Sobrescreva toString() em classes Java para gerar representações úteis em string para logging e depuração.
toString é o método que System.out.println, a concatenação de strings e o seu depurador chamam quando precisam renderizar um objeto como texto. O padrão — herdado de Object — é o nome da classe mais um hash hexadecimal ilegível. Sobrescrever toString é uma das coisas mais baratas e de maior retorno que você pode fazer para as pessoas que vão ler seus logs e stack traces (frequentemente uma versão futura de você).
De onde é chamado
Você quase nunca chama toString diretamente. Ele é invocado implicitamente sempre que um objeto é convertido em texto:
Point p = new Point(3, 4);
System.out.println(p); // calls p.toString()
String msg = "got " + p; // calls p.toString()
log.info("point: {}", p); // calls p.toString()Em qualquer lugar onde um Object aparece em um contexto que espera uma String, toString é executado. É por isso que o padrão é tão decepcionante — ele aparece em todo lugar e não te diz nada útil.
O padrão
Object.toString() retorna getClass().getName() + "@" + Integer.toHexString(hashCode()). Então uma classe simples imprime algo como com.example.Point@1540e19d. A classe está boa; o hex é o hash code de identidade, que é inútil quando você está tentando descobrir o que os campos do objeto contêm.
Uma boa sobrescrita
Um bom toString é curto, inequívoco e contém os dados que um leitor gostaria de ver:
@Override
public String toString() {
return "Point[x=" + x + ", y=" + y + "]";
}Algumas convenções que valem a pena seguir:
- Inclua o nome da classe. Quando uma linha de log mistura objetos, saber que tipo é importa.
- Use uma forma estável e analisável.
Class[field=value, field=value]é o que records e a maioria das bibliotecas Java modernas usam. É legível e fácil de buscar com grep. - Mostre os campos que importam — geralmente os que estão em
equals. Pule campos enormes (um blob de 10MB, umaConnection). - Não inclua segredos. Senhas, tokens, PII — deixe-os de fora, ou mascare-os.
toStringacaba em arquivos de log.
Com String.format ou blocos de texto
Para mais de três ou quatro campos, String.format (ou um bloco de texto) fica mais legível do que concatenação:
@Override
public String toString() {
return String.format("User[id=%d, name=%s, role=%s]", id, name, role);
}Não deixe ser custoso
toString é chamado com mais frequência do que você imagina — frameworks de log renderizam argumentos de forma lazy, mas suficientemente eager para que um toString O(n²) em uma lista de 100k elementos definitivamente deixe o seu serviço lento. Mantenha a saída limitada. O toString de uma coleção deve ser restrito a um head razoável, com ... (N more) para o restante.
Não lance exceções em toString
Lançar uma exceção em toString transforma uma linha de log rotineira em uma NullPointerException em algum lugar inesperado — e o problema subjacente que você estava tentando registrar se perde. Proteja-se contra nulls:
@Override
public String toString() {
return "Order[id=" + id + ", customer=" + (customer == null ? "<none>" : customer.name()) + "]";
}Records ganham um de graça
Se sua classe é um portador de dados simples, records já geram um bom toString:
record Point(int x, int y) {}
System.out.println(new Point(3, 4)); // Point[x=3, y=4]Portanto, sobrescrever toString é algo que você faz principalmente para classes que não são records.
Um exemplo completo
O que vem a seguir
toString produz uma descrição de um objeto. O próximo capítulo é sobre como produzir uma cópia de um — clone, Cloneable, e as escolhas de design complicadas em torno de cópias rasas versus profundas. Continue em Clonagem de objetos em Java.