在实现toString()方法时使用反射有意义吗?

49
@Override 
public String toString() { 
    return new Gson().toJson(this);
}

我这样为我的模型对象设置默认行为是否违反了一些良好的实践、"Joshua-模式"、一般设计模式或其他约定?

toString() 在我们当前使用的编程范式(Android)中,无论如何都只会在调试时使用。这也是我喜欢在 JSON 中看到对象的原因,因为许多 ORM/JSON 持久性将通过 http->php/python->mysql 和本地 SQLite 进行。


5
你没有破坏任何东西。toString()可以返回任何字符串(但是在相同的实例上每次调用它时,它应该返回相同的字符串,除非该实例的内部状态发生了改变)。 - Sotirios Delimanolis
2
@SotiriosDelimanolis:如果实例的内部状态发生变化,就不是这样了。 - JB Nizet
1
@JBNizet 是的,我应该用言语表达出来(即抄袭您的评论)。 - Sotirios Delimanolis
1
@tortal 我是在一般情况下说的。当然,Gson会做到这一点。 - Sotirios Delimanolis
4
需要翻译的内容:There are quite a few possible issues to watch for - expensive load-on-demand collections (performance), bi-directional references (endless loop), root object of huge object tree (whole database in the debug statement) and so on. That is the reason that in general case I do not do generic & reflection based toString(). Not sure if any such scenario applies here though.可能会遇到一些问题,如昂贵的按需加载集合(性能)、双向引用(无限循环)、巨大对象树的根对象(在调试语句中引入整个数据库)等等。这就是通常情况下我不使用基于通用和反射的toString()的原因。不确定是否适用于此场景。 - Michal
显示剩余2条评论
4个回答

91

可以使用GSON/Jackson/Reflections库来实现toString()方法。

有几种实现toString方法的方式:

  1. Reflections(Apache库)

  2. @Override
    public String toString(){
        return org.apache.commons.lang3.builder.ReflectionToStringBuilder.toString(this);
    }
    
  3. 基于JSON的实现(使用GSON、Jackson库)

  4. // 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;
    }
    
  5. ToStringBuilder(可在apache-commons库中获得)

    @Override
    public String toString() {
        return new org.apache.commons.lang3.builder.ToStringBuilder(this).
            append("field1", field1).
            append("field2", field2).
            toString();
    }
    
  6. toString()方法的高级实现

  7. @Override
    public String toString() {
        return new StringBuilder()
            .append("field1:"+field1)
            .append("field2:"+field2)
            .toString();
    }
    
  8. Lombok注解:在编译时生成toString()方法

  9. import lombok.ToString;
    
    @ToString
    public class ToStringExample {}
    

3
非常棒的答案,非常详细,提供了几种选项,并且用很少的文字。这个回答应该被接受。 - maaw
2
答案可以通过讨论 toString 的契约以及 JSON 序列化是否符合该契约(我认为是的)以及任何潜在的不足之处来改进(性能?但由于契约,这已得到缓解 - 该方法用于调试/显示,因此不应期望具有高性能) - Rich
1
是的,我在考虑使用Jackson与仅使用纯反射打印对象状态时的性能问题。使用Jackson似乎有些浪费,只是为了打印出一个对象。选项1中的代码也更加简洁。 - Kevin M

12

这种方法没有问题。我建议你为你的Gson实例创建一个静态变量,并启用漂亮的输出:

这样做没有任何问题。我建议您为Gson实例创建一个静态变量并启用漂亮的打印格式:

static Gson gson = new GsonBuilder().setPrettyPrinting().create();

这样toString方法的输出将被格式化。


为什么要使用新的GsonBuilder().setPrettyPrinting(),而不是直接使用"new Gson()"呢? - Biscuit Coder
3
经过一些谷歌搜索,我明白了。本应该先这样做。使用漂亮的打印会非常有用。 - Biscuit Coder
我猜你想表达的是 toJson(obj)toString() 不会打印出任何有用的东西。 - Hesam

7
注意:如果在您的toString()方法中使用GSon漂亮打印,那么在调试器中它将看起来像垃圾,因为它会充满换行符。
(对不起,我的声望还不够高,无法在上面进行评论)

不确定您考虑的是哪个调试器,但 IntelliJ 和可能其他 IDE 都可以很好地处理 toString() 输出中的换行符。 - Rich
1
许多年后,我意识到这个人实际上有一个非常好的观点。 - tortal

2

由于Gson使用内省来确定要打印哪些字段,因此这对性能不利。

除此之外,我认为它还好。虽然这不是标准的Java toString实现,但我认为更改它不会成为反模式。


4
您的回答除了“我认为”之外,没有提供任何证据或论据,这并不是很有用 :-( - Rich
1
@tdraun 是的,内省的代价很高,所以你是正确的,还有其他的考虑因素。那个 Rich 人只是说了一些幼稚的猜测,也就是胡扯。 - Alex Byrth
2
我同意,你的答案很好。没有必要抨击你的回答,因为你包含了“我认为”这几个词语。 - tortal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接