23

假设我有一个这样的类(并且还进一步假设所有私有变量:

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

        ...
}

现在,如果我想构建这个类的 toString() 表示,我会在 Item 类中做这样的事情:

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

但是如果我在类中有 15 个私有变量呢?我必须像这样写每个变量的名称吗?

理想情况下,我想通过遍历此类的私有变量列表并构造字符串表示来完成任务:

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

或者也许是一个 toJSON 方法,我仍然需要访问这些变量的名称。有什么建议么?

4

7 回答 7

37

你可以这样做:

@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();
}

不要使用字符串连接来构造 15 个数据成员的最终结果,尤其是toString()在调用很多时。内存碎片和开销可能非常高。用于StringBuilder构造大型动态字符串。

我通常让我的 IDE(IntelliJ)简单地toString()为我生成方法,而不是为此使用反射。

另一个有趣的方法是使用Project Lombok 中的 @ToString 注释

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;
    }
  }
}

我发现这比 Jakarta Commons toString 构建器更可取,因为这种方法更具可配置性,而且它也是在编译时而不是运行时构建的。

于 2009-11-23T03:06:20.063 回答
8

检查这个 API org.apache.commons.lang.builder.ToStringBuilder,它提供了多种方法来创建 toString 使用反射或不反射。也看看其他子类。

于 2009-11-23T03:02:45.043 回答
6

有这么一个api,叫Java Reflection

要完成您的要求,您可以简单地执行以下操作:

 Class<?> cls = this.getClass();
 Field fieldlist[] = cls.getDeclaredFields();
 for (Field aFieldlist : fieldlist) {
   // build toString output with StringBuilder()
 }
于 2009-11-23T03:08:04.687 回答
4

这应该正是您正在寻找的

    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();
}
于 2009-11-23T03:21:57.593 回答
2

toString大多数 IDE 提供了一种在给定类中创建方法的方法。

给定一个Item具有多个字段的类:

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

例如,在 Eclipse 中,对Item上面的类执行“Generate toString()”功能将创建以下内容:

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

使用反射将允许以编程方式观察自己的字段,但反射本身是一个相当昂贵(阅读:缓慢)的过程,因此除非确实需要,否则使用toString在运行时编写的固定方法可能会更可取。

于 2009-11-23T03:05:54.510 回答
1

您可能会遇到速度问题。如果您使用反射,与运行本机代码相比,它可能真的很慢。如果您要使用反射,那么您可能应该在内存中缓存您要迭代的变量。

于 2009-11-23T03:07:35.723 回答
-1

好的 medopal 答案开始,您可以使用此代码来获得对象所有字段的字符串表示形式,即使您在其类之外(显然它仅适用于具有 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();
}
于 2010-12-06T17:01:35.900 回答