80

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

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

在Item 15: Minimize mutability , 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;
    }
}

或者更接近structC 中的一个:

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 的更多支持可能会变得越来越流行?

4

7 回答 7

79

何时首选 JavaBeans

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

首选不可变 POJO

  • 你有少量的简单属性
  • 您不必与假设 JavaBean 约定的环境交互
  • 克隆对象时很容易(或至少可能)复制状态
  • 你根本不打算克隆对象
  • 你很确定你不必像上面那样修改属性的访问方式
  • 你不介意听到关于你的代码不够“面向对象”的抱怨(或冷笑)
于 2010-08-18T11:00:06.833 回答
40

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

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

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

  • 较少需要同步。

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

我真的在尝试转向不可变对象而不是 JavaBean 样式类。通过 getter 和 setter 暴露对象的内脏可能不应该是默认选择。

于 2010-08-18T11:29:33.470 回答
19

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

考虑到你的人,有很多领域,比如名字、中间名、姓氏、出生日期、家庭成员、教育、工作、薪水等。

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

如果您使用的是不可变 POJO,则获取一个代表她的 Person 对象,然后创建一个新的 Person 对象,将所有未更改的属性以及新的姓氏传递给该对象,并保存它。

如果它是一个 Java bean,您只需执行 setLastName() 并保存它。

它是“最小化可变性”而不是“从不使用可变对象”。在某些情况下,使用可变对象效果更好,决定使对象可变是否更适合您的程序确实是您的工作。您不应该总是说“必须使用不可变对象”,而是在开始伤害自己之前看看可以使多少类不可变。

于 2010-08-18T10:41:45.213 回答
8

总结其他答案,我认为:

  • 不变性有助于正确性(结构可以通过引用传递,并且您知道故障/恶意客户端不会破坏任何内容)和代码简单性
  • 可变性促进同质性:Spring 和其他框架创建一个没有参数的对象,设置对象属性,然后。使用相同的类来提供数据和保存修改也使接口更容易(你不需要get(id): Clientand save(MutableClient),作为 MutableClient 的一些后代 Client.

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

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

于 2010-08-18T11:29:55.893 回答
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;
    }
}
于 2014-09-23T08:02:22.687 回答
1

老实说,我认为不可变对象不会那么受欢迎。

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

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

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

毫无疑问,在某些问题领域,不变性非常有用,并且确实被使用了。但我认为当前的默认值似乎是可变的,因为这是大多数人所期望的,并且只有在具有明显优势的情况下才是不可变的。

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

于 2010-08-18T10:39:00.680 回答
0

在 Java 编程方面是不可变的:一旦创建的东西永远不应该改变状态,无论是预期的还是意外的!

这种技术在防御性编程中很有用,在这种编程中另一个实体不能引起状态的变化。

您不期望更改的示例:外部系统(单线程或多线程)从您的层获取引用并对对象进行操作并有意或无意地更改它。现在它可能是一个 POJO 或一个集合或一个对象引用,并且您不希望它发生变化或者您想要保护数据。你肯定会让对象不可变作为防御技术

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

于 2020-05-02T12:13:54.093 回答