java - resueltos - relaciones entre clases programacion orientada a objetos
Java: obtener las propiedades de una clase para construir una representación de cadena (7)
A partir de la buena respuesta medopal , puede usar este código para tener una representación en cadena de todos los campos de un objeto, incluso si está fuera de su clase (obviamente, solo es para campos con un método de obtención):
/**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();
}
Digamos que tengo una clase como esta (y también asumo que todas las variables privadas:
public class Item {
private String _id = null;
private String _name = null;
private String _description = null;
...
}
Ahora, si quiero construir una representación toString () de esta clase, haría algo como esto dentro de la clase Item.
@Override
public String toString() {
return (_id + " " + _name + " " + _description);
}
Pero ¿y si tengo 15 variables privadas dentro de la clase? ¿Tengo que escribir el nombre de cada variable como esta?
Idealmente, me gustaría terminar con la tarea iterando 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, todavía necesitaría acceso a los nombres de estas variables. ¿Alguna sugerencia?
Esto debería ser exactamente lo que estás 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();
}
Hay tal api, y se llama Reflexión de Java.
Para lograr lo que está solicitando, simplemente puede hacer algo como:
Class<?> cls = this.getClass();
Field fieldlist[] = cls.getDeclaredFields();
for (Field aFieldlist : fieldlist) {
// build toString output with StringBuilder()
}
La mayoría de los IDE proporcionan una forma de crear un método toString
en una clase determinada.
Dada una clase de 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 función "Generar a Cadena ()" en la clase de Item
anterior creará lo siguiente:
@Override
public String toString() {
return "Item [i=" + i + ", j=" + j + ", k=" + k + ", l=" + l + ", m="
+ m + ", n=" + n + ", o=" + o + "]";
}
El uso de la reflexión permitiría una forma programática de observar los propios campos, pero la reflexión en sí es un proceso bastante costoso (leer: lento), por lo que, a menos que sea realmente necesario, el uso de un método fijo de toString
escritura escrito en tiempo de ejecución probablemente será más deseable.
Podrías hacerlo:
@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 use la concatenación de cadenas para construir un resultado final a partir de 15 miembros de datos, en particular si toString()
se llamará mucho. La fragmentación de la memoria y los gastos generales pueden ser muy altos. Utilice StringBuilder
para construir grandes cadenas dinámicas.
Por lo general, obtengo mi IDE (IntelliJ) para generar simplemente métodos toString()
para mí en lugar de usar la reflexión para esto.
Otro enfoque interesante es usar la anotación @ToString del Proyecto 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; } } }
Considero que esto es mucho más preferible, por ejemplo, a los constructores de Jakarta Commons toString porque este enfoque es mucho más configurable y también se construye en tiempo de compilación y no en tiempo de ejecución.
Puede encontrarse con el problema de la velocidad. Si está utilizando la reflexión, puede ser muy lento, en comparación con la ejecución de código nativo. Si va a utilizar la reflexión, entonces probablemente debería tener una memoria caché en la memoria de las variables que desea repetir.
Verifique esta API org.apache.commons.lang.builder.ToStringBuilder , proporciona múltiples formas de crear una reflexión de inflexión de usString o sin reflexión. Eche un vistazo a otras subclases también.