toString()
Quizas alguien (como yo) tenga que inspeccionar las propiedades en algun punto de sus codigo sin tener que realizar un debug, puede ser cuando obtienes la iformacion de una DB a un pojo por ejemplo, cuando recibes la informacion de un Request, cuando recuperas la informacion de algun otro lugar, el chiste es que necesitas ver e estado de tus variables. Bueno en mi caso voy a trabajar con clases que tienen aprox 50 propiedades cada una (no voy a discutir si eso bueno o malo, las voy a usar y punto) pero por experiencia se que a veces puede aterarse la informacion, mas que nada en la transportacion de los datos cuand lees de un fichero, cuando encriptas y "desencriptas", cuando lo mandas por HTTP y mas qe nada hay broncas con la codificacion o que llego mocho el valor... etc
Bueno, ese es mi caso y coo yo no seré el unico que le mete mano al sistema quise sobreescribirle el toString() para que al invocarlo te muesre todas las propiedades que tiene la clase junto con su valor que almacena, de esa forma me evito hacerle el debug e inspeccionar una por una y e mi caso que son muchas propiedades pues me seria bien tedioso hacerlo
Este codigo usa reflexion y busca todas las propiedades de clase y las lista junto con e valor que tiene asignado, pero pues en mi caso tambien quise darle una alineacion paraqe el valor de las propiedades fuera mas legible
Yo defino por ejepo estas propiedades
aaaaaaaaa = "tengo valor",
bbbbbbb = "yo tabien tengo valor",
ccccccccccccccc,
ddddddddd,
eeeeeeee = "tienes el",
fffffff = "valor",
ggggg = "o",
hhhh = "te vale",
i,
jj = "ME VALE!!",
k;
private double
qqq = 645.318,
www = 123.13,
eee = 654,
rrr = 7722177,
ttt = 666,
yyy = 69;
private File
zzzqwe = new File("."),
xxxdgnf = new File("/"),
cccdsdfgvy = new File("c:/mi/carpeta"),
vvasdasvfdvkmg = new File("/tu/carpeta"),
bbasdasdbdhfrth = new File("/home/sweet/home"),
netewewwwnnert = new File("/asd/qwe.sh"),
mmmkjfrygsdfg = new File("/home/work/asd.waa");
Y esto se veria asi:
bbbbbbb............yo tabien tengo valor
ccccccccccccccc....null
ddddddddd..........null
eeeeeeee...........tienes el
fffffff............valor
ggggg..............o
hhhh...............te vale
i..................null
jj.................ME VALE!!
k..................null
qqq................645.318
www................123.13
eee................654.0
rrr................7722177.0
ttt................666.0
yyy................69.0
zzzqwe..............
xxxdgnf............\
cccdsdfgvy.........c:\mi\carpeta
vvasdasvfdvkmg.....\tu\carpeta
bbasdasdbdhfrth....\home\sweet\home
netewewwwnnert.....\asd\qwe.sh
mmmkjfrygsdfg......\home\work\asd.waa
y el codigo es este:
* Escribo mi propio metodo {@code toString()} porque quiero ver todas las propiedades
* de mi case en la frma que yo quiero
*/
@Override
public String toString () {
StringBuilder sb = new StringBuilder ();
try {
Class c = Class.forName (this.getClass ().getName ());
Field[] f = c.getDeclaredFields ();
int l = getMaxLenght (f) + 4;
for(Field propiedad:f)
sb.
append (rellenaEspacios (propiedad.getName (), l, '.')).
append (propiedad.get (this)).
append ("\n");
} catch (Throwable e) { e.printStackTrace (); }
return sb.toString ();
}
/**
* Metodo que obtiene la longitud maxima de las propiedades de la clase
*
* @param f Arreglo de tipo {@link Field} que encapsula lainformacion de
* las propiedades de la clase
*
* @return retorna el valor de la longitud maxima de un nombre de propiedad
*/
private int getMaxLenght(Field[] f){
int max = 0;
for(Field field:f)
max = field.getName ().length () > max ? field.getName ().length ():max;
return max;
}
/**
* Este metodo se encarga de poner el relleno en para que en la visualizacion se vean
* alineadas las propiedades de la clase
*
* @param s Define la cadena a rellenar
* @param l Define la longitud final que debe tener la cadena de retorno
* @param r Define el caracter de relleno
* @return
*/
private String rellenaEspacios(String s, int l, char r){
if(s == null || s.isEmpty () || l <= s.length ()) return s;
StringBuilder sb = new StringBuilder (s);
for(int indice = s.length (); indice < l; indice++) sb.append (r);
return sb.toString ();
}
Pues espro que a alguien le sirva y claro, recuerden que este lo hice a mis necesidades y si quieren que haga mas monerias pus' ahi tienen el codigo!
Por cierto, a mi me gustaria usarlo esto e varias clases, pero eso decopiar y pegar como qe no va muy bien. Si alguie ha utilizado http://projectlombok.org/ sabrá que por ejemplo le pone:
y por lo que lei que cuando compila ya le genero el suguiente codigo:
y asi con el @Getter y varias cosas mas... Bueno el punto es que si alguien sabe hacerlo y le interesa este codigo pues vamos!!
- java.daba.doo's blog
- Inicie sesión o regístrese para enviar comentarios
Comentarios
commons beanutils
http://www.benmccann.com/dev-blog/easy-java-bean-tostring-using-beanutils/
No me convence mucho
bueno, ya la revise eso de BeanUtils, no sabia que existía y bueno principalmente se trata de una librería para no manejar crudamente la reflexión. Su forma de describir las propiedades es en una cadena corridita que igual resultaría un poco tedioso de leer, por ejemplo, le puse esto a un bean
y lo que me pinta es algo asi:
Ahora habrá que tomar en cuenta que quiero vigilar aprox 50 propiedades y si tomamos en cuenta que la mayoria son Strings pues seguro que se nos pierden las propiedades entre los valores mostrados.
Acabo de darme cuenta que
Acabo de darme cuenta que BeanUtil solo detecta las propiedades que tengan los métodos de acceso, si no los tiene no aparecen en el describe()
Alguna vez hice algo así y la
+1 Alguna vez hice algo así y la verdad me fue medio mal con el performance.
Un "boost" es obtener todas los atributos y dejarlos en algún caché, no tiene caso buscarlos siempre si siempre van a ser los mismos:
Field [] fields = this.getClass().getFields();
for( f in fiedls ) { ....
}
Puede ser:
public String toString() {
for( f in fields ) etc. etc
}
Aunque he de aclarar que esto fue por allá de Java 1.2 y desde entonces el performance ha mejorado muchísimo en la parte de reflection.
Oscar te dare una respuesta
Oscar te dare una respuesta valida (pero me la acabo de sacar de la manga) es que en una clase si la modificas por reflect pues ya no vale lo que tenias al cargar la clase por lo que te ves obligado a refrescar tu lista de metodos y propiedades (en caso de modificarla con reflect)... (fin de la respuesta sacada de la manga)
la verdadera razón por la que la metí en el método fue porque al hacer la impresión de mis propiedades me aparecian esas cosas de Fields[] y Class (cosa que no me gustaba y asi, simplemente por eso las meti (pfffffts)
Y justo por lo del rendimiento le metí StringBuilder para no afectar el rendimiento con Strings que son inmutables y bajo las cobijas hacen un despapaye cuando haces:
Ahora con tipo de dato
Estuve moviendole a esto porque queria tambien mostrar los tipos de datos de mis propiedades. pero pues cando los metia sucedia se perdia el orden asi que decidi meterle tambien una mascara para seguir teniendo todo bie alineadito... ah, tambien agregue el nombre dela clase pues eso debe ser básico!
ahora se ve asi:
String aaaaaaaaa..........tengo valor
String bbbbbbb............yo tabien tengo valor
String ccccccccccccccc....null
String ddddddddd..........null
String eeeeeeee...........tienes el
String fffffff............valor
String ggggg..............o
String hhhh...............te vale
String i..................null
String jj.................ME VALE!!
String k..................null
double qqq................645.318
double <a href="http://www................123.13
double" title="www................123.13
double">www................123.13
double</a> eee................654.0
double rrr................7722177.0
double ttt................666.0
double yyy................69.0
File zzzqwe..............
File xxxdgnf............\
File cccdsdfgvy.........c:\mi\carpeta
File vvasdasvfdvkmg.....\tu\carpeta
File bbasdasdbdhfrth....\home\sweet\home
File netewewwwnnert.....\asd\qwe.sh
File mmmkjfrygsdfg......\home\work\asd.waa
Este es el nuevo codigo agregand lo que ya mencione:
* Escribo mi propio metodo {@code toString()} porque quiero ver todas las propiedades
* de mi case en la frma que yo quiero
*/
@Override
public String toString () {
StringBuilder sb = new StringBuilder (this.getClass ().getName ());
try {
Field[] f = Class.forName (this.getClass ().getName ()).getDeclaredFields ();
int lt = getMaxLenght (f, 't');
int lp = getMaxLenght (f, 'p') + 4;
for(Field ff:f)
sb.
append ("\n").
append (rellenaEspacios (ff, lp, lt, '.')).
append (ff.get (this));
} catch (Throwable e) { e.printStackTrace (); }
return sb.toString ();
}
/**
* Metodo que obtiene la longitud maxima de las propiedades de la clase, verifica el tipo
* de longitud que se desea obtener con el uso del parametro t
*
* @param f Arreglo de tipo {@link Field} que encapsula lainformacion de
* las propiedades de la clase
* @param t Define el tipo de longitud que se desea obtener. Actualmente se soporta
* 'p' para los nombres de las propiedades y 't' para los tipos
*
* @return retorna el valor de la longitud maxima de un nombre de propiedad
*/
private int getMaxLenght(Field[] f, char t){
int max = 0;
for(Field ff:f)
switch (t){
case 'p':
if(ff.getName ().length () > max) max = ff.getName ().length ();
break;
case 't':
if(ff.getType ().getSimpleName ().length () > max)
max = ff.getType ().getSimpleName ().length ();
break;
default: return -1;
}
return max;
}
/**
* Este metodo se encarga de poner el relleno en para que en la visualizacion se vean
* alineadas las propiedades de la clase
*
* @param s Define la cadena a rellenar
* @param lp Define la longitud final que debe tener la cadena de retorno
* @param lp Define la longitud del tipo mas largo
* @param r Define el caracter de relleno
* @return
*/
private String rellenaEspacios(Field f, int lp, int lt, char r) throws Exception{
if(f.getName () == null || f.getName ().isEmpty () || lp <= f.getName ().length ())
return f.getName ();
StringBuilder sb = new StringBuilder (
String.format (
"%" + lt + "s %s",
f.getType ().getSimpleName (),
f.getName ()));
for(int i = f.getName ().length (); i < lp; i++) sb.append (r);
return sb.toString ();
}
No es mucho relajo
¿No creen que es mucho relajo para un toString()? En lo personal me gusta tener mis objetos con el toString(), es muy util, pero pues para ahorrarme unos ciclos del procesador lo hago a patin aun así sean n propiedades, simplemente uso un buen editor de texto y con un replace y en un par de minutos tengo mi toString() como lo quiero, ademas a veces en lo particular no quiero mostrar todas las propiedades, solo las mas representativas.
@java.daba.doo. No me refiero
@java.daba.doo.
No me refiero al valor, sino al los campos en si mismos. Es decir, cuando tienes declarado:
int i ;
}
El atributo i ya no puede desaparecer de la clase en tiempo de ejecución ( al menos no de una forma "natural" de Java, por ahí se puede instrumentar la clase y quizá con un classloader customizado re-cargarla pero estamos hablando de casos normales )
Entonces, si ya no se puede quitar ( ni poner una nueva ) no le veo mucho caso pedirla siempre. Lo que definitivamente si es necesario es obtener el valor.
Ahora, lo que te decía es que de Java 1.2 para acá esas cosas mejoraron muchísimo su desempeño, es muy probable que ya se haga un cache interno.
@luiguisf:
Si es mucho relajo, pero no se propone que se haga así en toooodas las clases de todos los proyectos. Puede ser útil en ciertas ocasiones y está más como demo de reflection para ayudar a revisar los objetos.
Algo que se le podría hacer de forma adicional, es hacer que todo ese computo sea opcional ( dependiendo de una configuracion ) y encapsular la lógica en una sola clase para no tener que hacer ( tanto ) copy/paste:
Por ejemplo
String nombre;
...20 atributos más
public String toString() {
return ToString.isEnabled() ? ToString.toString( this ) : "Empleado.nombre = " + nombre;
}
}
class Departamento {
String nombre;
...
public String toString() {
return ToString.isEnabled() ? ToString.toString( this ) : "Departamento.nombre = " + nombre;
}
}
Y definir la utilería "ToString" como:
private static Logger logger = Logger.getLogger(ToString.class.getName() );
public static boolean isEnabled() {
return logger.isLoggable( Level.FINEST );
}
public static String toString( Object o ) {
for( Field f : o.getClass().getFields() ) {
stringBuilder.append( ....
}
}
}
Que me es precisamente lo algo como lo que hace el BeanUtils, pero ya no revisé si era exactamente lo que quería Jdd.
Es que yo soy bie flojo y no
Es que yo soy bie flojo y no me gusta hacer la misma tarea manual muchas veces. Por es lo hice, de hecho lo que dices es justo lo que venia haciendo pero ya me aburrio eso...
Bueno, aqui esta e codigo para otros "flojos" como yo y para los muy "chambeadores" pueden seguir talacheando:
1.- (llenar de System.out.println() su codigo) copiar el texto
2.- Copiar la salida
3.- Pegarlo en el editor de texto (una ventana mas)
4.- Definir los criterios dereemplazo y darle reemplazar
5.- Seguir en lo qe estabas
Nah, yo si soy rete flojo y no o haria, por eso digo: que se canse la maquina ¿yo por que?
por cierto, si esto lo pones en un Logger y defines el nivel de "traza" o no se como venga pero que sea el de menos importancia, lo activas y desactivas cuando se te antoje xon solo reemplazar una cosita algo asi como:
...
logger.setLevel(Levels.NOMAS_ERRORES)
...
logger.setLevel(Levels.NOMAS_ADVERTENCIAS)
pero eso de los loggers: es oootra histoooooria!
Regresando: si es mucho relajo, por eso menciono que me gustaria una anotacion para solo escribirla a la clase y solito te haga el override... aunque por ahora no se como hacerlo (ya o investigare)
Osea que quiero que se vea asi:
@ToString
public class MiClase {
...
Eso si seria lo optimo, de hecho hasta se ve lindo... Saludos!
Estabatan aferrado a las
Estabatan aferrado a las anotaciones que olvide pensar en eso que dices Oscar
Pasa que estaba tan aferrado a que fosozamente hiciera override a su metodo toString() por eso lo definia asi con @ToString pero mientras esto que dices es muy adecuado
ah y a o que me eferia e java es que puedas agregarle Fields a la clase.. por eso decia que lo que cargues a inico de la clase despues de una manipulacion de Reflect (si afecta el nombre, elimina o agrega una propiedad de la clase)... bueno yo he visto que en tiempo de ejecucion se pueden crean una clase completita desde "{" hasta "}"
:p
Problemas
Oscar: pues tendra que ser dentro de la misma clase porque cuando lo ejcuto con metodos privados me sale una linda Excepcion
doo.daba.java.chacharas.reflect.ListaPropiedades
String aaaaaaaaa..........
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.reflect.Field.doSecurityCheck(Field.java:960)
at java.lang.reflect.Field.getFieldAccessor(Field.java:896)
at java.lang.reflect.Field.get(Field.java:358)
at doo.daba.java.util.ToString.toString(ToString.java:28)
at doo.daba.java.chacharas.reflect.ListaPropiedades.toString(ListaPropiedades.java:54)
at doo.daba.java.chacharas.reflect.ListaPropiedades.main(ListaPropiedades.java:60)
y si no lepongo modificador de acceso sale lo mismo:
bueno lo checo despues
Ahh si pero eso ya no es
Sobre los private... ahh pos si verdad? .. Creo que lo que yo hice entonces fue obtener los métodos que empezaran con get.
Ahh si pero eso ya no es reflection es instrumentación, es modificar el bytecode y demás pero incluso entonces solo lo puedes hacer una vez antes de que la clase se cargue. Usando algo como JavaRebel se puede volver a cargar y demás.
Lo que mencionas podría ser una anotación + AOP ( Enrique escribió sobre esto ) se podría modificar el toString como dices.
Ahora que pensandolo bien, si se va a modificar el bytecode y sabemos que atributos tiene la clase, mejor escribirlos para que se impriman estáticamente.
:) :) No te interesaría mejor ayudarme a esto: http://bit.ly/Ryz-language :)
Aunque la implementación que estoy haciendo es "contra-natura" jejeje
Lombok ya lo hace
La librería que mencionas al final, Lombok, ya te crea un método que te muestra los campos en el toString:
http://projectlombok.org/features/ToString.html
Yo de un tiempo a esta parte uso la librería bastante y hasta he implementado una anotación propia, aunque de momento solo la versión javac por que generar código en tiempo de compilación para Eclipse es un lío de cuidado :).
@OscarRyz La verdad no le se
@OscarRyz La verdad no le se a esas cosas y si habia checado lo de AOP que habia implementado sobre jAlarms, de hecho es justo la entrada que voy a estudiarme (a fondo) en cuanto tenga el tiempito
@greeneyed si dehecho en eso me inspire pero pues lo muestra en una linea plana (cosa qe no me gusta) y fue por eso que hice esto
No es que sea chambeador
A los 5 pasos a que te refieres pues solo se hace cuando creas/modificas la clase y no veo el para que se use System.out.println() si lo que quiero es el nombre de la propiedad no el valor, ni siquiera hay que correr la clase. En lo personal prefiero los atajos de la IDE y listo lo hace en automatico, ademas de que podemos definir un template para que el toString() se genere a nuestro gusto. De esta forma bajamos algo el acoplamiento.