为什么我不应该使用不可变的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): Client
和save(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; } }