En esta ocasión les quiero platicar de una fantástica clase de utilidad que yo he utilizado con frecuencia en el desarrollo de software, Esta clase es ReflectionToStringBuilder la cual es parte de la librería commonds-lang de Apache fundation. Mediante esta clase podemos imprimir de forma muy rápida las propiedades de toda una clase con solo una línea de código. Seguramente en varias ocasiones has tenido que imprimir mediante System.out.println( myObject.getPropiedad() ) las propiedades de una clase para depurar los valores o los has tenido que imprimir directamente en el Log de la aplicación como logger.debug( myObject.getPropiedad() ). En tal caso tenemos que realizar esto por cada propiedad de la clase que deseamos depurar. Si eres de los que hace esto con regularidad entonces este Artículo te interesa.
Como funciona:
ReflectionToStringBuilder es una clase que nos permitirá representar cualquier objeto mediante una cadena de String. Esta clase tiene la cualidad de introspectar nuestras clases para determinar las propiedades que tiene, y de esta forma representar a la clase con todas sus propiedades en forma de un String con formato. La librería de Commonds-lang nos proporciona por default 7 formatos distintos para representar nuestros objetos, las cuales se en listan a continuación:
- DEFAULT_STYLE: Muestra el nombre de nuestra clase totalmente calificada junto a todas sus propiedades en formato com.MyClass[prop1=valor, prop2=valor, … ].
- JSON_STYLE: Representa a nuestra clase en el famoso formato JSON, el cual tiene un formato {“propiedad“: “valor“}
- MULTI_LINE_STYLE: Mi método favorito, este presenta los valores de forma muy similar al método DEFAULT_STYLE, pero este utiliza una nueva línea para representar cada propiedad, además de utilizar tabuladores para dar un mejor formato a la salida.
- NO_CLASS_NAME_STYLE: Este trabaja igual que DEFAULT_STYLE pero no muestra el nombre de la clase, en su lugar solo muestra sus propiedades.
- NO_FIELD_NAMES_STYLE: Este método trabaja igual que DEFAULT_STYLE, pero no muestra el nombre de las propiedades, en su lugar solo muestra los valores de las propiedades separados por una coma, com.MyClass[val1,val2,…] .
- SHORT_PREFIX_STYLE: Funciona igual que DEFAULT_STYLE, pero esta solo muestra el nombre simple de las clase sin el paquete al que pertenece.
- SIMPLE_STYLE: Este método solo muestra los valores de las propiedades separados por coma. En este formato no se muestra el nombre de la clase ni el nombre de las propiedades.
Un punto importante a resaltar cuando utilizamos ReflectionToStringBuilder es que todos los objetos que están dentro del objeto a procesar son representados mediante su propio método toString(), por lo que todas las propiedades que no son de un tipo primitivo o de una clase wrapper, no se mostrarán de la forma que esperaríamos. En vez de ver el nombre de la clase y sus propiedades variamos la posición en memoria del objeto. Por ejemplo MyObjeto@234d232. Para solucionar esto, Commonds-lang proporciona un estilo adicional llamado RecursiveToStringStyle. Este método recorre de forma recursiva la estructura para representar todos los objetos internos mediante ReflectionToStringBuilder con la única limitante que todos los valores son representados mediante el método DEFAULT_STYLE.
Sin más preámbulos veamos un ejemplo de cómo utilizarlo. El código lo puedes descargar del siguiente repositorio de https://github.com/oscarjb1/ReflectionToStringBuilder.git
Analizaremos un ejemplo simple. Crearemos una clase Customer la cual se ve de la siguiente manera:
package com.oscarblancarte.tostring; import java.io.Serializable; public class Customer{ private String name; private String tel; private Address address; /** GET & SET*/ }
La clase Customer tiene un nombre, un teléfono y una dirección. El nombre y el teléfono son String por lo que son mostrados si ningún problema, pero la clase Address es una clase Custom, por lo que sus valores se mostrarán según el estilo utilizado.
La clase Address se ve de la siguiente manera:
package com.oscarblancarte.tostring; public class Address { private String address1; private String address2; /** GET & SET */ }
La clase Address es más simple, solo tiene tipos simples por lo que sus valores serán correctamente mostrados en todos los estilos.
Finalmente la clase ejecutable ToStringCommondsLang
package com.oscarblancarte.tostring; import org.apache.commons.lang3.builder.RecursiveToStringStyle; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; public class ToStringCommonsLang { public static void main(String[] args) { Customer customer = createCustomer(); System.out.println("DEFAULT_STYLE ....."); System.out.println(ReflectionToStringBuilder.toString(customer, ToStringStyle.DEFAULT_STYLE)); System.out.println("\nJSON_STYLE ....."); System.out.println(ReflectionToStringBuilder.toString(customer, ToStringStyle.JSON_STYLE)); System.out.println("\nMULTI_LINE_STYLE ....."); System.out.println(ReflectionToStringBuilder.toString(customer, ToStringStyle.MULTI_LINE_STYLE)); System.out.println("\nNO_CLASS_NAME_STYLE ....."); System.out.println(ReflectionToStringBuilder.toString(customer, ToStringStyle.NO_CLASS_NAME_STYLE)); System.out.println("\nNO_FIELD_NAMES_STYLE ....."); System.out.println(ReflectionToStringBuilder.toString(customer, ToStringStyle.NO_FIELD_NAMES_STYLE)); System.out.println("\nSHORT_PREFIX_STYLE ....."); System.out.println(ReflectionToStringBuilder.toString(customer, ToStringStyle.SHORT_PREFIX_STYLE)); System.out.println("\nSIMPLE_STYLE .....\n"); System.out.println(ReflectionToStringBuilder.toString(customer, ToStringStyle.SIMPLE_STYLE)); System.out.println("\nRecursiveToStringStyle .....\n"); System.out.println(ReflectionToStringBuilder.toString(customer, new RecursiveToStringStyle() )); } private static Customer createCustomer(){ Customer customer = new Customer(); customer.setName("Oscar Blancarte"); customer.setTel("+51 4343343434"); customer.setAddress(new Address()); Address addres = customer.getAddress(); addres.setAddress1("Dirección primaria del cliente"); addres.setAddress2("Dirección sucundaria del cliente"); return customer; } }
El método createCustomer se encargará únicamente de crear una instancia de la clase Customer, la cual tendrá una relación con un objeto de la clase Address.
En el método main ya podemos ver como utilizamos la clase ReflectionToStringBuilder para imprimir nuestro objeto en los distintos formatos. Para iniciar ejecutemos esta clase como una aplicación de escritorio para ver el siguiente resultado.
DEFAULT_STYLE ..... com.oscarblancarte.tostring.Customer@61bbe9ba[name=Oscar Blancarte,tel=+51 4343343434,address=com.oscarblancarte.tostring.Address@511d50c0] JSON_STYLE ..... {"name":"Oscar Blancarte","tel":"+51 4343343434","address":com.oscarblancarte.tostring.Address@511d50c0} MULTI_LINE_STYLE ..... com.oscarblancarte.tostring.Customer@61bbe9ba[ name=Oscar Blancarte tel=+51 4343343434 address=com.oscarblancarte.tostring.Address@511d50c0 ] NO_CLASS_NAME_STYLE ..... [name=Oscar Blancarte,tel=+51 4343343434,address=com.oscarblancarte.tostring.Address@511d50c0] NO_FIELD_NAMES_STYLE ..... com.oscarblancarte.tostring.Customer@61bbe9ba[Oscar Blancarte,+51 4343343434,com.oscarblancarte.tostring.Address@511d50c0] SHORT_PREFIX_STYLE ..... Customer[name=Oscar Blancarte,tel=+51 4343343434,address=com.oscarblancarte.tostring.Address@511d50c0] SIMPLE_STYLE ..... Oscar Blancarte,+51 4343343434,com.oscarblancarte.tostring.Address@511d50c0 RecursiveToStringStyle ..... com.oscarblancarte.tostring.Customer@61bbe9ba[name=Oscar Blancarte,tel=+51 4343343434,address=com.oscarblancarte.tostring.Address@511d50c0[address1=Dirección primaria del cliente,address2=Dirección sucundaria del cliente]]
El método toString de ReflectionToStringBuilder esta sobrecargado por lo que ofrece distintas formas de invocarlo, puede consultar los distintos métodos en la documentación oficial aquí. Para este ejemplo, solo enviamos el objeto a imprimir y el estilo a utilizar. El único método diferente al resto es el último, en el cual utilizamos la clase RecursiveToStringStyle para imprimir de forma recursiva los objetos internos de la clase Customer. Observemos que este método es el único que muestra correctamente los valores de la clase Address dentro del Customer.
Otra de las cosas interesantes que tiene esta librería es que podemos implementar nuestros propios estilos extendiendo la clase ToStringStyle.