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

时间:2010-08-18 10:33:04

标签: java immutability javabeans pojo

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

但是在Java世界中,使用JavaBeans似乎更常见,我无法理解为什么我应该使用它们。就个人而言,如果代码只处理不可变对象而不是一直改变状态,那么代码看起来会更好。

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

如果我将对象Person实现为 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中的struct

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”,并保持简单。

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

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

7 个答案:

答案 0 :(得分:76)

首选

时更喜欢JavaBeans
  • 您必须与期望他们的环境进行互动
  • 你有很多属性,在实例化时进行所有初始化都不方便
  • 你的状态很昂贵或因某些原因无法复制但需要变异
  • 您认为在某些时候您可能需要更改属性的访问方式(例如,从存储位置移动到计算值,访问权限等)。
  • 你想要遵守编码标准,盲目地坚持使用JavaBeans以某种方式更“面向对象”

时更喜欢不可变的POJO
  • 您有少量简单属性
  • 您不必与假定JavaBean约定的环境进行交互
  • 克隆对象时很容易(或者至少可能)复制状态
  • 你根本没有计划克隆对象
  • 您非常确定您不必修改上述属性的访问方式
  • 你不介意听到抱怨(或嘲笑)你的代码如何不充分“面向对象”

答案 1 :(得分:38)

我很惊讶 Thread 这个词在本次讨论中没有出现过。

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

这不仅可以使您的编码更容易,而且还可以为您带来两个性能优势:

  • 减少同步需求。

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

我真的想要转向不可变对象而不是JavaBean样式类。通过getter和setter公开对象的内容可能不是默认选择。

答案 2 :(得分:16)

这取决于你想要做什么。如果您使用持久层,并从数据库中获取某些行到POJO,并且您想要更改属性并将其保存回来,那么使用JavaBean样式会更好,特别是如果您有很多属性。

考虑一下你的人,有很多领域,比如,中,姓,出生日期,家庭成员,教育,工作,工资等。

那个人碰巧是刚刚结婚并被接受更改姓氏的女性,你需要更新数据库。

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

如果它是Java bean,你可以直接执行setLastName()并保存它。

这是'最小化可变性'而不是'永远不会使用可变对象'。有些情况下可变对象的效果更好,确定一个对象是否可变更适合你的程序确实是你的工作。你不应该总是说“必须使用不可变对象”,而是在你开始伤害自己之前看看有多少个类可以变成不可变的。

答案 3 :(得分:7)

总结其他答案我认为:

  • Inmutability促进正确性(结构可以通过引用传递,你知道任何东西都不会被错误的/恶意客户端破坏)和代码简单性
  • 可变性促进同质性:Spring和其他框架创建一个没有参数的对象,设置对象属性和voilà。还可以使用相同的类更轻松地使用相同的类来提供数据和保存修改(您不需要get(id): Clientsave(MutableClient),因为MutableClient是客户端的后代。

如果存在中间点(create,set properties,make inmutable),那么框架可能会鼓励更多的不可改变的方法。

无论如何,我建议将不可变对象视为“只读Java Bean”,强调如果你是一个好孩子并且不触及那个危险的setProperty方法,那一切都会好的。

答案 4 :(得分:3)

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

答案 5 :(得分:2)

我不认为不可变对象会得到所有流行的东西,说实话。

我确实看到了这些优点,但是像Hibernate和Spring这样的框架目前非常流行(并且也有很好的理由),并且它们最适合使用bean。

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

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

肯定存在不变性非常有用且确实使用的问题领域。但我认为目前的默认似乎是可变的,因为这是大多数人的预期,只有具有明显优势的情况下才是不可改变的。

虽然确实可以在Spring中使用带有参数的构造函数,但它似乎是一种将遗留和/或第三方代码与您一起使用的全新Spring代码。至少这是我从文档中提到的。

答案 6 :(得分:0)

就Java编程而言,这是不可改变的:一旦创建,就永远不会发生状态改变,无论是预期的还是意外的!

该技术在防御性编程中非常有用,在该防御性编程中,另一个实体无法引起状态变化。

您不希望更改的示例:外部系统(单线程或多线程),它从您的层获取引用并在该对象上运行并有意或无意地对其进行了更改。现在它可能是POJO或集合或对象引用,并且您不希望更改或希望保护数据。您肯定会把该对象作为防御技术变为不变

您期望更改的示例:确实不需要不变性,因为它会妨碍正确的编程过程。