Métodos de objetos de Java: toString ()

M

 

Introducción

En este artículo comenzaré una serie de artículos que describen los métodos que a menudo se olvidan de la clase Object base del lenguaje Java. A continuación se muestran los métodos del objeto Java base, que están presentes en todos los objetos Java debido a la herencia implícita de Object. Se incluyen enlaces a cada artículo de esta serie para cada método a medida que se publican los artículos.

  • toString (estás aquí)
  • getClass
  • es igual a
  • código hash
  • clon
  • finalizar
  • esperar y notificar

En las secciones que siguen, describiré cuáles son estos métodos, sus implementaciones básicas y cómo anularlos cuando sea necesario. El enfoque de este primer artículo es el toString() método que se utiliza para dar una representación de cadena que identifica una instancia de objeto y transmite su contenido y / o significado en forma legible por humanos.

El método toString ()

A primera vista el toString() El método puede parecer un método bastante inútil y, para ser honesto, su implementación predeterminada no es muy útil. Por defecto el toString() devolverá una cadena que enumera el nombre de la clase seguido de un signo @ y luego una representación hexadecimal de la ubicación de memoria a la que se ha asignado el objeto instanciado.

Para ayudar en mi discusión de los omnipresentes métodos de objetos Java, trabajaré con un simple Person clase, definida así:

package com.adammcquistan.object;

import java.time.LocalDate;

public class Person {
    private String firstName;
    private String lastName;
    private LocalDate dob;
    
    public Person() {}
    
    public Person(String firstName, String lastName, LocalDate dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = dob;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public LocalDate getDob() {
        return dob;
    }

    public void setDob(LocalDate dob) {
        this.dob = dob;
    }
}

Junto con esta clase tengo un rudimentario Main class para ejecutar los ejemplos que se muestran a continuación para presentar las características de la implementación base de toString().

package com.adammcquistan.object;

import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        Person me = new Person("Adam", "McQuistan", LocalDate.parse("1987-09-23"));
        Person me2 = new Person("Adam", "McQuistan", LocalDate.parse("1987-09-23"));
        Person you = new Person("Jane", "Doe", LocalDate.parse("2000-12-25"));
        System.out.println("1. " + me.toString());
        System.out.println("2. " + me);
        System.out.println("3. " + me + ", " + you);
        System.out.println("4. " + me + ", " + me2);
}

La salida se ve así:

1. [email protected]
2. [email protected]
3. [email protected], [email protected]
4. [email protected], [email protected]

Lo primero que hay que mencionar es que la salida de las líneas uno y dos es idéntica, lo que muestra que cuando pasa una instancia de objeto a métodos como print, println, printf, así como madereros, el toString() se llama implícitamente al método.
Además, esta llamada implícita a toString() también ocurre durante la concatenación como se muestra en la salida de la línea 3.

Ok, ahora es el momento de que intervenga mi propia opinión personal cuando se trata de las mejores prácticas de programación de Java. ¿Qué le parece potencialmente preocupante sobre la línea 4 (en realidad, cualquiera de los resultados)?

Es de esperar que esté respondiendo con una pregunta en este sentido, “bueno, Adam, es bueno que la salida me diga el nombre de la clase, pero ¿qué diablos voy a hacer con esa dirección de memoria chillona?”.

Y yo respondía con “¡Nada!”. Es 99,99% inútil para nosotros como programadores. Una idea mucho mejor sería que anulemos esta implementación predeterminada y proporcionemos algo que sea realmente significativo, como esto:

public class Person {
    // omitting everyting else remaining the same

    @Override
    public String toString() {
        return "<Person: firstName=" + firstName + ", lastName=" + lastName + ", dob=" + dob + ">";
    }
}

Ahora, si vuelvo a ejecutar la clase principal anterior, obtengo la siguiente salida muy mejorada:

1. <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23>
2. <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23>
3. <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23>, <User: firstName=Jane, lastName=Doe, dob=2000-12-25>
4. <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23>, <User: firstName=Adam, lastName=McQuistan, dob=1987-09-23>

¡DIOS MIO! ¡Algo que pueda leer! Con esta implementación, ahora tengo una gran posibilidad de poder comprender lo que está sucediendo en un archivo de registro. Esto es especialmente útil cuando el soporte técnico está gritando sobre un comportamiento errático relacionado con las instancias de People en el programa para el que estoy enganchado.

Advertencias para implementar y usar toString ()

Como se muestra en la sección anterior, implementar un informativo toString() en sus clases es una idea bastante buena, ya que proporciona una forma de transmitir de manera significativa el contenido y la identidad de un objeto. Sin embargo, hay ocasiones en las que querrá adoptar un enfoque ligeramente diferente para implementarlos.

Por ejemplo, supongamos que tiene un objeto que simplemente contiene demasiado estado para empaquetarlo en la salida de un toString() método o cuando el objeto contiene principalmente una colección de métodos de utilidades. En estos casos, a menudo es recomendable generar una descripción simple de la clase y sus intenciones.

Considere la siguiente clase de utilidad sin sentido que encuentra y devuelve la persona más antigua de una lista de objetos Personas.

public class OldestPersonFinder {
    public List<Person> family;
    
    public OldestPersonFinder(List<Person> family) {
        this.family = family;
    }

    public Person oldest() {
        if (family.isEmpty()) {
            return null;
        }
        Person currentOldest = null;
        for (Person p : family) {
            if (currentOldest == null || p.getDob().isAfter(currentOldest.getDob())) {
                currentOldest = p;
            }
        }
        return currentOldest;
    }

    @Override
    public String toString() {
        return "Class that finds the oldest Person in a List";
    }
}

En este caso, no sería muy útil recorrer la colección completa de Person objetos en la familia List miembro de instancia y construya una cadena ridículamente grande para devolver que represente cada Person. En cambio, es mucho más significativo devolver una cadena que describa las intenciones de la clase que en este caso es encontrar el Person Quien es el más viejo.

Otra cosa que me gustaría sugerir es que se asegure de proporcionar acceso a toda la información específica de los datos de su clase que incluya en la salida de su toString() método.

Digamos, por ejemplo, que no he proporcionado un método getter para mi Person clase dob miembro en un vano intento de mantener en secreto la edad de la persona. Desafortunadamente, los usuarios de mi Person la clase eventualmente se darán cuenta de que simplemente pueden analizar la salida de la toString() método y adquirir los datos que buscan de esa manera. Ahora, si alguna vez cambio la implementación de toString() Estoy casi seguro de romper su código. Por otro lado, permítanme decirles que, en general, es una mala idea analizar la toString() salida por esta misma razón.

Conclusión

Este artículo describió los usos y el valor de los a menudo olvidados toString() método de la clase Object base de Java. He explicado el comportamiento predeterminado y he dado mi opinión sobre por qué creo que es una mejor práctica implementar su propio comportamiento específico de clase.

Como siempre, gracias por leer y no dude en comentar o criticar a continuación.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con tus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad