我有很多数据对象,我希望能够生成一个表示每个对象的String
,而不需要为每个对象实现toString
方法。
我正在考虑获得字段及其值的反射。
还有其他想法吗?
谢谢。
欢迎您使用来自jakarta的ToStringBuilder。它有两种模式,一种需要使用API添加所有需要的字段,另一种是基于反射的:
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
使用JUtils插件在包级别生成toString方法
或者使用反射,你可以尝试这样做:
public static void toString(Object object) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException{
Class c = object.getClass();
Method[] methods = c.getMethods();
for (Method method : methods) {
if(method.getName().startsWith("get")){
System.out.println(method.getName()+" -> " +method.invoke(object, null));
}
}
}
JUtil插件将是最好的,因为它将允许您稍后更改对象的toString,如果您需要提供有关该对象的更多信息。
如果您有很多对象,那么不要在每个类中添加toString
,而是使用代码插装(您可以在jar中更改.class或在加载时使用javaagent更改它们)。例如,AspectJ会有帮助,但是还有很多其他的选择。例如,你可以做这样的事情(使用AspectJ和ToStringBuilder从Apache Commons):
@Aspect
public class ToStringAspect {
@Around("execution(String *.toString()) && target(t) && within(your.target.package.*)")
public String toStringCall(Object t) {
return ToStringBuilder.reflectionToString(t);
}
}
ToStringBuilder非常灵活。例如,如果你不需要类名,可以设置StandardToStringStyle#setUseClassName(false):
public String toStringCall(Object t) {
StandardToStringStyle style = new StandardToStringStyle();
style.setUseClassName(false);
style.setUseIdentityHashCode(false);
ToStringBuilder builder = new ReflectionToStringBuilder(t, style);
return builder.toString();
}
有几种方法可以实现toString方法。
-
Reflections (Apache库)
@Override public String toString(){ return org.apache.commons.lang3.builder.ReflectionToStringBuilder.toString(this); }
-
基于JSON的实现(GSON, Jackson libraries)
// GSON library for JSON @Override public String toString(){ return new com.google.gson.Gson().toJson(this); } // Jackson libabry for JSON/YAML @Override public String toString() { try { return new com.fasterxml.jackson.databind.ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this); } catch (com.fasterxml.jackson.core.JsonProcessingException e) { e.printStackTrace(); } return null; }
-
ToStringBuilder(可与apache-commons库一起使用)
@Override public String toString() { return new org.apache.commons.lang3.builder.ToStringBuilder(this). append("field1", field1). append("field2", field2). toString(); }
-
硬核toString()实现
@Override public String toString() { return new StringBuilder() .append("field1:"+field1) .append("field2:"+field2) .toString(); }
toString
方法由Java中的每个类继承自java.lang.Object
。我不明白你怎么能避免不实现(实际上覆盖)Object.toString
。此外,方法是为每个类定义的,而不是为每个对象定义的。
您可以使用继承层次结构来解决这个问题,通过让您的类从覆盖默认toString
实现的公共类继承。这个实现可以使用反射来反射类的字段(不是父类,而是当前对象的类),并生成字符串。
如果您根本不想覆盖该方法,还可以研究字节码编织的使用。就我个人而言,我不认为在字节码编织上投入的努力是不值得的,特别是当您可以覆盖toString
时。
某种静态字符串dataToString(Data data)
方法可能会起作用。我对反射一无所知,所以这是我的想法。
方法的实现取决于数据对象的设置方式。有遗传吗?如果有的话,您可以将超类类型的对象作为参数传入,并对所有数据全局使用该方法。您需要在方法中执行一些条件检查或其他形式的逻辑,以返回正确的字符串,具体取决于您的程序是如何设置的。
关于你的程序的更多信息(它是如何设置的,你想要打印什么,数据对象是什么,等等)将帮助我使这个答案更好:)
如果您想为每个对象实现toString(),您可以使用AspectJ。在这样的事情之外,您将被困在为每个对象实现方法。
如前所述,您可以使用已经实现的库,如Apache commons-lang中的ReflectionToStringBuilder。如前所述。
或者自己用反射API编写类似的smt
是一些示例:
class ReflectionAnalyzer {
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;
}
}