为什么我不应该使用不可变的POJO而不是JavaBeans?

现在我已经实现了一些Java应用程序,到目前为止只有桌面应用程序。 我更喜欢使用不可变对象来在应用程序中传递数据,而不是使用带有mutators(setter 和getters )的对象,也称为JavaBeans。

但在Java世界中,使用JavaBeans似乎更为常见,我不明白为什么要使用它们。 就个人而言,如果代码只处理不可变对象,而不是始终改变状态,代码看起来会更好。

项目15:最小化可变性Effective Java 2ed中也build议使用不可变对象。

如果我有一个实现为JavaBean的对象,它将如下所示:

 public class Person { private String name; private Place birthPlace; public Person() {} public setName(String name) { this.name = name; } public setBirthPlace(Place birthPlace) { this.birthPlace = birthPlace; } public String getName() { return name; } public Place getBirthPlace() { return birthPlace; } } 

和作为不可变对象实现的相同Person

 public class Person { private final String name; private final Place birthPlace; public Person(String name, Place birthPlace) { this.name = name; this.birthPlace = birthPlace; } public String getName() { return name; } public Place getBirthPlace() { return birthPlace; } } 

或者更接近于C:

 public class Person { public final String name; public final Place birthPlace; public Person(String name, Place birthPlace) { this.name = name; this.birthPlace = birthPlace; } } 

我也可以让不可变对象中的getter来隐藏实现细节。 但是因为我只用它作为一个struct我宁愿跳过“getters”,并保持简单。

简单地说,我不明白为什么使用JavaBean更好,或者如果我可以并应该继续使用我的不可变的POJO?

许多Java库似乎对JavaBean有更好的支持,但是随着时间的推移,对不可变POJO的更多支持可能会更受欢迎?

首选JavaBeans时

  • 你必须与期望它们的环境进行交互
  • 你有很多的属性,这将是不便的做实例的所有初始化
  • 你有状态是昂贵的或不可能复制出于某种原因,但需要突变
  • 你认为在某些时候你可能不得不改变属性的访问方式(例如,从存储到计算值,访问授权等)
  • 你想遵守那些无意识地坚持它使用JavaBeans更“面向对象”的编码标准

喜欢不可变的POJO时

  • 你有一些简单的属性
  • 假设JavaBean约定,您不必与环境进行交互
  • 克隆你的对象时很容易(或者至less可以)复制状态
  • 你根本不打算克隆这个对象
  • 你很确定你永远不必修改如何访问属性如上
  • 你不介意听到你的代码不够“面向对象”的呜呜声(或嘲笑)

我很惊讶Thread这个词没有出现在这个讨论中的任何地方。

不可变类的主要好处之一是由于没有可变的共享状态,它们本质上是更线程安全的。

这不仅使你的编码更容易,它还会给你两个性能优势作为副作用:

  • 较less需要同步。

  • 更多的使用最终variables的范围,这可以促进后续的编译器优化。

我真的试图转向不可变对象而不是JavaBean样式类。 通过getter和setter暴露对象的内部可能不应该是默认的select。

那么这取决于你想要做什么。 如果你使用一个持久层,并且从数据库中取一些行到一个POJO中,并且想要改变一个属性并保存,那么使用JavaBean风格会更好,尤其是如果你有很多属性。

考虑一下你的人有很多领域,比如说第一,中,姓,出生date,家庭成员,教育,工作,薪水等等。

而那个人恰好是一个刚刚结婚并且接受她的姓氏改变的女性,你需要更新数据库。

如果你正在使用不可变的POJO,你可以获取一个代表她的Person对象,然后创build一个新的Person对象,将所有你没有改变的属性和新的姓氏传递给它,并保存它。

如果它是一个Java bean,你可以做setLastName()并保存它。

这是“最小化可变性”,而不是“从不使用可变对象”。 有些情况下可变对象的效果更好,确定是否让对象更易于更好地适合您的程序是您的工作。 你不应该总是说'必须使用不可变的对象',而应该在你开始伤害自己之前看看有多less类可以变得不可变。

总结其他答案我认为:

  • 可交换性有利于正确性 (结构可以通过引用传递,而且你知道什么都不会被有故障/恶意的客户端破坏)并且代码简单
  • 可变性有利于同质性 :Spring和其他框架创build一个没有参数的对象,设置对象属性和voilà 。 也可以使用相同的类来提供数据和保存修改(不需要get(id): Clientsave(MutableClient) ,作为客户端的MutableClient一些后代,使接口更容易。

如果有一个中间点(创build,设置属性,使之不可变),也许框架会鼓励更多的不可变的方法。

无论如何,我build议在不可改变的对象思考“只读Java豆”强调的一点是,如果你是一个好孩子,不要触及危险的setProperty方法,一切都会好起来的。

说实话,我不认为不可变的东西会stream行起来。

我确实看到了优势,但是像Hibernate和Spring这样的框架目前非常stream行(也是一个很好的原因),而且它们对于bean来说确实是最好的。

所以我不认为不变性是不好的,但它肯定会限制你的整合选项与当前的框架。

编辑评论提示我澄清我的答案了一下。

那里肯定有不可变性是非常有用的问题领域,并且确实被使用。 但是我认为目前的违约似乎是可以改变的,因为这是大多数人所期望的,而且如果这样做有明显的优势,它就是不变的。

尽pipe在Spring中使用带有参数的构造函数确实是可能的,但它似乎意图作为使用遗留代码和/或第三方代码的一种方式来使用漂亮的全新Spring代码。 至less这是我从文档中find的。

从Java 7开始,你可以拥有不变的bean,两全其美。 在你的构造函数中使用注解@ConstructorProperties。

 public class Person { private final String name; private final Place birthPlace; @ConstructorProperties({"name", "birthPlace"}) public Person(String name, Place birthPlace) { this.name = name; this.birthPlace = birthPlace; } public String getName() { return name; } public Place getBirthPlace() { return birthPlace; } }