Pregunta

Estaba escribiendo un toString () para una clase en Java el otro día escribiendo manualmente cada elemento de la clase en un String y se me ocurrió que usando la reflexión podría ser posible crear un método genérico toString () eso podría funcionar en TODAS las clases. ES DECIR. averiguaría los nombres y valores de los campos y los enviaría a una Cadena.

Obtener los nombres de los campos es bastante simple, esto es lo que se le ocurrió a un compañero de trabajo:

public static List initFieldArray(String className) throws ClassNotFoundException {

    Class c = Class.forName(className);
    Field field[] = c.getFields();
    List<String> classFields = new ArrayList(field.length);

    for (int i = 0; i < field.length; i++) {
        String cf = field[i].toString();
        classFields.add(cf.substring(cf.lastIndexOf(".") + 1));
    }

    return classFields;
}

Utilizando una fábrica, podría reducir la sobrecarga de rendimiento almacenando los campos una vez, la primera vez que se llama a toString (). Sin embargo, encontrar los valores podría ser mucho más costoso.

Debido al rendimiento de la reflexión, esto puede ser más hipotético que práctico. Pero me interesa la idea de la reflexión y cómo puedo usarla para mejorar mi programación diaria.

¿Fue útil?

Solución

Apache commons-lang ReflectionToStringBuilder hace esto por usted.

import org.apache.commons.lang3.builder.ReflectionToStringBuilder

// your code goes here

public String toString() {
   return ReflectionToStringBuilder.toString(this);
}

Otros consejos

Otra opción, si está de acuerdo con JSON, es la biblioteca GSON de Google.

public String toString() {
    return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}

Hará la reflexión por ti. Esto produce un archivo JSON agradable y fácil de leer. Al ser fácil de leer, las personas que no son tecnológicas pueden encontrar el JSON intimidante.

También puede hacer que GSONBuilder sea una variable miembro, si no desea renovarlo cada vez.

Si tiene datos que no se pueden imprimir (como una secuencia) o datos que simplemente no desea imprimir, puede agregar etiquetas @Expose a los atributos que desea imprimir y luego usar la siguiente línea.

 new GsonBuilder()
.setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation()
.create()
.toJson(this);

W / reflexión, ya que no conocía la biblioteca apache:

(tenga en cuenta que si hace esto, probablemente tendrá que lidiar con los subobjetos y asegurarse de que se impriman correctamente, en particular, las matrices no le mostrarán nada útil)

@Override
public String toString()
{
    StringBuilder b = new StringBuilder("[");
    for (Field f : getClass().getFields())
    {
        if (!isStaticField(f))
        {
            try
            {
                b.append(f.getName() + "=" + f.get(this) + " ");
            } catch (IllegalAccessException e)
            {
                // pass, don't print
            }
        }
    }
    b.append(']');
    return b.toString();
}


private boolean isStaticField(Field f)
{
    return Modifier.isStatic(f.getModifiers());
}

Si está utilizando Eclipse, también puede echar un vistazo a JUtils toString generator , que lo hace estáticamente (generando el método en su código fuente).

No es reflejo, pero eché un vistazo a la generación del método toString (junto con equals / hashCode) como un paso posterior a la compilación utilizando la manipulación de bytecode. Los resultados fueron mixtos.

Puede usar bibliotecas ya implementadas, como ReflectionToStringBuilder de Apache commons-lang. Como se mencionó.

O escriba smt similar por usted mismo con API de reflexión .

Aquí hay algún ejemplo :

class UniversalAnalyzer {

   private ArrayList<Object> visited = new ArrayList<Object>();

   /**
    * Converts an object to a string representation that lists all fields.
    * @param obj an object
    * @return a string with the object's class name and all field names and
    * values
    */
   public String toString(Object obj) {
      if (obj == null) return "null";
      if (visited.contains(obj)) return "...";
      visited.add(obj);
      Class cl = obj.getClass();
      if (cl == String.class) return (String) obj;
      if (cl.isArray()) {
         String r = cl.getComponentType() + "[]{";
         for (int i = 0; i < Array.getLength(obj); i++) {
            if (i > 0) r += ",";
            Object val = Array.get(obj, i);
            if (cl.getComponentType().isPrimitive()) r += val;
            else r += toString(val);
         }
         return r + "}";
      }

      String r = cl.getName();
      // inspect the fields of this class and all superclasses
      do {
         r += "[";
         Field[] fields = cl.getDeclaredFields();
         AccessibleObject.setAccessible(fields, true);
         // get the names and values of all fields
         for (Field f : fields) {
            if (!Modifier.isStatic(f.getModifiers())) {
               if (!r.endsWith("[")) r += ",";
               r += f.getName() + "=";
               try {
                  Class t = f.getType();
                  Object val = f.get(obj);
                  if (t.isPrimitive()) r += val;
                  else r += toString(val);
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }
         }
         r += "]";
         cl = cl.getSuperclass();
      } while (cl != null);

      return r;
   }    
}

Aquí está el equivalente de Netbeans a la respuesta de Olivier; complemento smart-codegen para Netbeans .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top