2009-11-23 10 views
20

Digamos que tengo una clase como esta (y también asumen, además, que todas las variables privadas:Java: Obtener las propiedades de una clase para la construcción de una representación de cadena

public class Item { 
    private String _id = null; 
    private String _name = null; 
    private String _description = null; 

     ... 
} 

Ahora, si quiero construir una toString() la representación de esta clase, me gustaría hacer algo como esto dentro de la clase de artículo:

@Override 
public String toString() { 
    return (_id + " " + _name + " " + _description); 
} 

Pero lo que si tengo que decir 15 variables privadas dentro de la clase ¿tengo que escribir el nombre de todos y cada uno? variable como esta?

Idealmente, me gustaría obtener más de la tarea por iteración a través de la lista de variables privadas de esta clase y construir la representación de cadena:

@Override 
public String toString() { 
    ArrayList<String> members = getClass().getMembers(); //Some method like this 
    String string = ""; 
    for(...) 
     string += members[i] + " "; 
} 

O tal vez un método toJSON, aun así necesitan tener acceso a la nombres de estas variables ¿Alguna sugerencia?

+0

Véase también [Auto-generar Método toString] (http://stackoverflow.com/questions/2653268/auto -generating-tostring-method) – Vadzim

Respuesta

31

que podría hacer:

@Override 
public String toString() { 
    StringBuilder sb = new StringBuilder(); 
    sb.append(getClass().getName()); 
    sb.append(": "); 
    for (Field f : getClass().getDeclaredFields()) { 
    sb.append(f.getName()); 
    sb.append("="); 
    sb.append(f.get(this)); 
    sb.append(", "); 
    } 
    return sb.toString(); 
} 

¿No concatenación utilización cadena para construir un resultado final de 15 miembros de datos, sobre todo si el toString() se llamará mucho. La fragmentación de la memoria y la sobrecarga podrían ser realmente altas. Use StringBuilder para construir grandes cadenas dinámicas.

Normalmente obtengo mi IDE (IntelliJ) para generar simplemente los métodos toString() en lugar de utilizar el reflejo para esto.

Otro enfoque interesante es el uso de la @ToString annotation from Project Lombok:

import lombok.ToString; 

@ToString(excludes="id") 
public class ToStringExample { 
    private static final int STATIC_VAR = 10; 
    private String name; 
    private Shape shape = new Square(5, 10); 
    private String[] tags; 
    private int id; 

    @ToString(callSuper=true, includeFieldNames=true) 
    public static class Square extends Shape { 
    private final int width, height; 

    public Square(int width, int height) { 
     this.width = width; 
     this.height = height; 
    } 
    } 
} 

Me parece mucho más preferible, por ejemplo, Jakarta Commons toString constructores ya que este enfoque es mucho más configurable y también se construyó en tiempo de compilación, no tiempo de ejecución.

+0

Gracias por esto. Me gustaría ir con el primer enfoque dependiendo de los resultados del perfil porque el segundo enfoque usa una biblioteca más. Puede que tenga que escribir el toJSON para solo tres o cuatro objetos, así que dejo que el perfil lo decida. – Legend

+2

+1 para el tiempo de compilación Ejemplo de Lombok –

+0

Para Lombok, solo necesita una biblioteca durante la compilación. Siempre y cuando no necesite @SneakyThrows, no hay dependencias de tiempo de ejecución en Lombok –

8

Compruebe esta API org.apache.commons.lang.builder.ToStringBuilder, proporciona múltiples formas de crear cadenas usando reflexión o sin reflexión. Eche un vistazo a otras subclases también.

+1

Gracias. Esto se ve muy interesante. Especialmente el método que no usa reflexión. – Legend

2

La mayoría de los IDE proporcionan una forma de crear un método toString en una clase determinada.

Dada una clase Item con múltiples campos:

class Item { 
    int i; 
    int j; 
    int k; 
    int l; 
    int m; 
    int n; 
    int o; 
} 

Por ejemplo, en Eclipse, la realización de "Generar toString()" función de la clase de Item anterior creará el siguiente:

@Override 
public String toString() { 
    return "Item [i=" + i + ", j=" + j + ", k=" + k + ", l=" + l + ", m=" 
      + m + ", n=" + n + ", o=" + o + "]"; 
} 

El uso de reflection permitiría una forma programática de observar los propios campos, pero la reflexión en sí misma es un proceso bastante caro (leer: lento), por lo que a menos que sea realmente necesario, utilice un método toString fijo w en tiempo de ejecución probablemente sea más deseable.

+0

En realidad, la única razón por la que estaba pensando en Reflection es porque necesito escribir una representación toString y una representación toJSON. Puedo escribirlos de forma manual, pero a veces las clases representan elementos de una base de datos por lo que podrían tener muchos campos. ¿Seguirías sugiriendo un enfoque manual? – Legend

+1

Realmente va a estar a la altura de los requisitos de los métodos toString y toJSON. Si el rendimiento no es una preocupación, entonces usar la reflexión probablemente no importará. Si el rendimiento es una preocupación, probablemente sea una buena idea perfilar el código escrito "manualmente" y el código escrito utilizando la reflexión, y ver si la diferencia va a importar. – coobird

+0

Entendido. Gracias por la explicación. Creo que necesitaré un rendimiento. Estoy escribiendo un servicio web REST, así que supongo que la respuesta está bastante clara en tu explicación. – Legend

1

Puede encontrarse con el problema de la velocidad. Si está utilizando el reflejo, puede ser realmente lento, en comparación con ejecutar código nativo.Si vas a utilizar la reflexión, entonces probablemente deberías tener un caché en la memoria de las variables sobre las que vas a iterar.

2

Hay una API, y se llama Java Reflection

Para lograr lo que usted está solicitando, puede simplemente hacer algo como:

Class<?> cls = this.getClass(); 
Field fieldlist[] = cls.getDeclaredFields(); 
for (Field aFieldlist : fieldlist) { 
    // build toString output with StringBuilder() 
} 
+0

Gracias por eso. – Legend

4

Esto debe ser exactamente lo que están buscando

public String toString() { 
    StringBuilder sb = new StringBuilder(); 
    try { 
     Class c = Class.forName(this.getClass().getName()); 
     Method m[] = c.getDeclaredMethods(); 
     Object oo; 

     for (int i = 0; i < m.length; i++) 
      if (m[i].getName().startsWith("get")) { 
       oo = m[i].invoke(this, null); 
       sb.append(m[i].getName().substring(3) + ":" 
         + String.valueOf(oo) + "\n"); 
      } 
    } catch (Throwable e) { 
     System.err.println(e); 
    } 
    return sb.toString(); 
} 
-1

a partir de la good medopal answer, puede utilizar este código para tener una representación de cadena de todos los campos de un objeto, incluso si está fuera de su clase (obviamente que es sólo para los campos con un método getter):

/**Gives a string representation of the fields of the object 
* @param obj the object 
* @return 
*/ 
private static String objectToString(Object obj) { 
    StringBuilder sb = new StringBuilder(); 
    try { 
     Class c = Class.forName(obj.getClass().getName()); 
     Method m[] = c.getDeclaredMethods(); 

     Object oo; 
     for (int i = 0; i < m.length; i++) 
      if (m[i].getName().startsWith("get")) { 
       oo = m[i].invoke(obj, null); 
       sb.append(m[i].getName().substring(3) + "=" 
         + String.valueOf(oo) + "\n"); 
      } 
    } catch (Throwable e) { 
     System.err.println(e); 
    } 
    return sb.toString(); 
} 
Cuestiones relacionadas