制作不可变的Java对象

时间:2013-08-12 18:33:09

标签: java immutability

我的目标是使Java对象不可变。我有一个班级Student。我用以下方式对其进行编码以实现不变性:

public final class Student {

private String name;
private String age;

public Student(String name, String age) {
    this.name = name;
    this.age = age;
}

public String getName() {
    return name;
}

public String getAge() {
    return age;
}

}

我的问题是,实现Student课程不变性的最佳方法是什么?

16 个答案:

答案 0 :(得分:66)

严格来说,你的课不是一成不变的,它只是有效的一成不变的。要使其不可变,您需要使用final

private final String name;
private final String age;

虽然差异看起来很微妙,it can make a significant difference in a multi-threaded context。不可变类本质上是线程安全的,只有在安全发布时,有效不可变类才是线程安全的。

答案 1 :(得分:56)

制作不可变类时必须考虑几件事情:

  • 上课final - 您已经
  • 制作所有字段privatefinal - 在代码中进行适当的更改
  • 不提供任何改变实例状态的方法
  • 如果您的班级中有可变字段,例如ListDate,那么让他们final就不够了。你应该从他们的getters返回一份防御性副本,这样他们的状态就不会被调用方法变异。

对于第4点,假设您的班级中有Date字段,那么该字段的getter应如下所示:

public Date getDate() {
    return new Date(this.date.getTime());
}

当你的可变字段本身包含一些可变字段,并且反过来可以包含一些其他可变字段时,制作防御性副本会变得很头疼。在这种情况下,您需要迭代地复制每个副本。我们将此可变字段的迭代副本命名为 Deep Copy

自己实施深层复制可能很麻烦。但是,将这个问题区分开来,一旦你发现自己陷入了制作深度防御性副本的要求,就应该再次考虑你的课堂设计。

答案 2 :(得分:4)

  1. 将该类声明为final,因此无法扩展。
  2. 将所有字段设为私有,以便不允许直接访问。
  3. 不要为变量提供setter方法
  4. 将所有可变字段设为最终字段,以使其值只能分配一次。
  5. 通过执行深层复制的构造函数初始化所有字段。
  6. 在getter方法中执行克隆对象以返回副本而不是返回实际的对象引用。
  7. source

答案 3 :(得分:3)

使用final关键字:

private final String name;
private final String age;

答案 4 :(得分:3)

将变量设为私有,并且没有setter方法可用于原始数据类型。如果我的班级有任何对象集合?

使用集合对象使任何类不可变?

使用extends collection类编写您自己的集合对象,并遵循私有变量而不遵循setter方法。或者返回集合对象的clone对象。

public final class Student {

private StudentList names;//Which is extended from arraylist

public Student() {
names = DAO.getNamesList()//Which will return All Student names from Database  its upto you how you want to implement.
}

public StudentList getStudentList(){
return names;//you need to implement your own methods in StudentList class to iterate your arraylist; or you can return Enumeration object.
}

public Enumeration getStudentNamesIterator(
Enumeration e = Collections.enumeration(names);
return e;
}

public class StudentList extends ArrayList {

}

答案 5 :(得分:2)

这很好,但我也会创建字段final

此外,我会将年龄设为intdouble而不是字符串。

答案 6 :(得分:2)

您的示例已经是不可变对象,因为Student类中的字段只能在实例初始化时设置。

要使对象不可变,您必须执行以下步骤:

  1. 不要使用任何可以更改班级字段的方法。例如,不要使用Setters。
  2. 避免使用公共非最终字段。如果您的字段是公共的,那么您必须将它们声明为final并在构造函数中或直接在声明行中初始化它们。

答案 7 :(得分:2)

稍微扩展答案。

finalImmutable不同,但如果您以某种方式使用它,则可以使用final使某些内容不可变。

某些类型是不可变的,因为它们代表不变的值而不是可变状态的对象。字符串,数字等是不可变的。最后,通常我们的对象归结为最终引用不可变值的数据结构,但我们通过为相同的字段名称分配新值来更改数据结构。

因此,为了使某些东西真正不可变,你需要确保最终一直使用final,直到你到达每个字段到达组合树底部的每个值。否则,某些东西可能会从你的对象下面改变,并且它不是真正完全不可变的。

答案 8 :(得分:1)

回答为时已晚,但可能有助于其他有这个问题的人。

  1. 构造后不能修改不可变对象的状态,任何修改都应该导致新的不可变对象。
  2. Immutable类的所有字段都应该是final。
  3. 必须正确构造对象,即在构建过程中对象引用不得泄漏。
  4. 对象应该是最终的,以便限制子类以改变父类的不变性。
  5. 我认为此链接有助于提供更多帮助 阅读更多:http://javarevisited.blogspot.com/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html#ixzz40VDQDDL1

答案 9 :(得分:0)

它已经是不可变的 - 一旦初始化它就无法更改内容,因为你还没有制作setter。您可以在变量中添加最终关键字。

答案 10 :(得分:0)

将所有变量设为final,并在设置某个字段时,使其返回对新Student对象的引用,并使用String中新设置的值。

答案 11 :(得分:0)

您可以按照此示例中显示的指南(Google中的第一个结果): http://www.javapractices.com/topic/TopicAction.do?Id=29

答案 12 :(得分:0)

这里有一些规则,它们有助于在Java中使类不可变: 1.构造后不能修改不可变对象的状态,任何修改都应该导致新的不可变对象。 2. Immutable类的所有字段都应该是final。 3.必须正确构造物体,即在施工过程中物体参考不得泄漏。 4.对象应该是最终的,以便限制子类来改变父类的不变性。

示例:

public final class Contacts {

private final String name;
private final String mobile;

public Contacts(String name, String mobile) {
    this.name = name;
    this.mobile = mobile;
}

public String getName(){
    return name;
}

public String getMobile(){
    return mobile;
}

}

请参阅此链接:http://javarevisited.blogspot.in/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html

答案 13 :(得分:0)

根据Strategy for Defining Immutable Objects

  1. 不提供“ SELECT t1.id, t1.name, t2.letter_count, t2.avg_number FROM table1 t1 INNER JOIN (SELECT t2.t1_id, AVG(t2.number) as avg_number FROM table2 t2 GROUP BY t2.t1_id ) t2 ON t2.t1_id = t1.id LEFT JOIN (SELECT t3.t2_id, COUNT(t3.letter) as letter_count FROM table3 t3 WHERE t3.letter IN ('a', 'b', 'c') GROUP BY t3.t2_id ) t3 ON t3.t1_id = t1.id ORDER BY t3.letter_count DESC, t2.avg_number DESC; ”方法-修改字段或 字段引用的对象。

  2. 设置所有字段setterfinal

  3. 不允许子类覆盖方法。最简单的方法是将该类声明为private

    一种更复杂的方法是将final设为私有并在工厂方法中构造实例。

  4. 如果实例字段包含对可变对象的引用,则不允许更改这些对象:

    不要提供修改可变对象的方法。

    b。不要共享对可变对象的引用。永远不要存储对传递给构造函数的外部可变对象的引用;如有必要,创建副本,并存储对副本的引用。同样,在必要时创建内部可变对象的副本,以避免在方法中返回原始对象。

答案 14 :(得分:0)

Java SE 16

您可以使用作为 Java SE 16 一部分引入的 JEP 395: Records 功能以简洁的方式创建不可变类。

如果您已经浏览了上面的链接,那么您一定已经想出可以简单地做到这一点

record Student(String name, String age) { }

反过来你得到的是:

  1. A final class Student
  2. 签名与标头 Student(String name, String age) 相同的规范构造函数。
  3. private final 字段、nameage 及其对应的具有相同名称和返回类型的 public 访问器方法。
  4. 自动创建 equalshashCodetoString 方法。

演示:

Student.java

record Student(String name, String age) { }

Main.java

class Main{
    public static void main(String[] args) {
        Student s1 = new Student("Bharat", "10 Years");
        Student s2 = new Student("Arvind", "10 Years");

        System.out.println(s1);
        System.out.println(s1.equals(s2));
        System.out.println(s1.age().equals(s2.age()));
    }
}

输出:

Student[name=Bharat, age=10 Years]
false
true

答案 15 :(得分:-1)

使类或变量成为最终的

Public final class constants
{
  private final String name;
  private final String mobile;

  // code
}