简体   繁体   English

制作不可变的 Java 对象

[英]Make immutable Java object

My goal is to make a Java object immutable.我的目标是使 Java 对象不可变。 I have a class Student .我有一个班级Student I coded it in the following way to achieve immutability:我以以下方式对其进行编码以实现不变性:

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

}

My question is, what is the best way to achieve immutability for the Student class?我的问题是,实现Student类不变性的最佳方法是什么?

Your class is not immutable strictly speaking, it is only effectively immutable.严格来说,您的类不是一成不变的,它只是有效地不可变。 To make it immutable, you need to use final :要使其不可变,您需要使用final

private final String name;
private final String age;

Although the difference might seem subtle, it can make a significant difference in a multi-threaded context .尽管差异可能看起来很微妙, 但它可以在多线程上下文中产生显着差异 An immutable class is inherently thread-safe, an effectively immutable class is thread safe only if it is safely published.不可变类本质上是线程安全的,有效的不可变类只有在安全发布时才是线程安全的。

There are few things that you must consider for making an immutable class:要创建一个不可变的类,您必须考虑以下几点:

  • Make your class final - You already have让你的课程final - 你已经有了
  • Make all the fields private and final - Make appropriate changes in your code将所有字段设为privatefinal字段 - 在代码中进行适当更改
  • Don't provide any methods that change the state of your instance不要提供任何改变实例状态的方法
  • If you have mutable fields in your class, like List , or Date , making them final won't suffice.如果您的类中有可变字段,例如ListDate ,将它们设为final不够的。 You should return a defensive copy from their getters , so that their state isn't mutated by calling methods.你应该从他们的getters返回一个防御性副本,这样他们的状态就不会因为调用方法而改变。

For the 4th point, say you have a Date field in your class, then the getter for that field should look like:对于第 4 点,假设您的类中有一个Date字段,那么该字段的 getter 应如下所示:

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

Making a defensive copy can become a headache, when your mutable field itself comprises of some mutable field, and that in turn can contain some other mutable field.当您的可变字段本身包含一些可变字段,而该可变字段又可以包含其他一些可变字段时,制作防御性副本可能会令人头疼。 In that case, you would need to make copy of each of them iteratively.在这种情况下,您需要迭代地复制它们中的每一个。 We name this iterative copy of mutable fields as Deep Copy .我们将这个可变字段的迭代副本命名为Deep Copy

Implementing deep copy by yourself may be cumbersome.自己实现深拷贝可能很麻烦。 But,keeping that issue apart, you should consider your class design again, once you see yourself falling into such requirement of making deep defensive copy.但是,把这个问题分开,你应该再次考虑你的类设计,一旦你发现自己陷入了制作深度防御性副本的要求。

How do you make a mutable object immutable?如何使可变对象不可变?

  1. Declare the class as final so it can't be extended.将类声明为 final 使其无法扩展。
  2. Make all fields private so that direct access is not allowed.将所有字段设为私有,以便不允许直接访问。
  3. Don't provide setter methods for variables不要为变量提供 setter 方法
  4. Make all mutable fields final so that it's value can be assigned only once.将所有可变字段设为 final,以便其值只能分配一次。
  5. Initialize all the fields via a constructor performing deep copy.通过执行深复制的构造函数初始化所有字段。
  6. Perform cloning of objects in the getter methods to return a copy rather than returning the actual object reference.在 getter 方法中执行对象克隆以返回副本而不是返回实际的对象引用。

source 来源

Why do we create immutable objects?我们为什么要创建不可变对象?
Immutable objects are simply objects whose state (the object's data) cannot change after construction.不可变对象只是其状态(对象的数据)在构造后无法更改的对象。

  • are simple to construct, test, and use易于构建、测试和使用
  • are automatically thread-safe and have no synchronization issues自动线程安全并且没有同步问题
  • don't need a copy constructor不需要复制构造函数
  • don't need an implementation of clone不需要克隆的实现
  • allow hashCode to use lazy initialization, and to cache its return value允许 hashCode 使用延迟初始化,并缓存其返回值
  • don't need to be copied defensively when used as a field用作字段时不需要防御性复制
  • make good Map keys and Set elements (these objects must not change state while in the collection)制作好的 Map 键和 Set 元素(这些对象在集合中不能改变状态)
  • have their class invariant established once upon construction, and it never needs to be checked again在构造时建立它们的类不变量,并且永远不需要再次检查
  • always have "failure atomicity" (a term used by Joshua Bloch): if an immutable object throws an exception, it's never left in an undesirable or indeterminate state始终具有“失败原子性”(Joshua Bloch 使用的术语):如果不可变对象抛出异常,则它永远不会处于不良或不确定状态

Source来源

With final keyword:使用final关键字:

private final String name;
private final String age;

Making variables private and no setter methods will work for primitive data types.将变量设为私有且没有 setter 方法将适用于原始数据类型。 If my class has any collection of objects?如果我的班级有任何对象集合?

To making any class immutable with collection object?使用集合对象使任何类不可变?

Write your own collection object with extends collection class and follow the private variables and no setter methods.使用 extends 集合类编写自己的集合对象,并遵循私有变量而不是 setter 方法。 or return clone object of your collection object.或返回您的集合对象的克隆对象。

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 {

}

This is fine but I would make the fields final as well.这很好,但我也会将这些字段设为final

Also I would make the age an int or double rather than a String.此外,我会将年龄intdouble而不是 String。

Expanding on the answer a bit.稍微扩展一下答案。

final is not the same as Immutable but you can use final to make certain things immutable if you use it in certain ways. finalImmutable但如果您以某种方式使用它,您可以使用final使某些事物不可变。

Certain types are immutable, in that they represent unchanging values rather than objects of changeable state.某些类型是不可变的,因为它们表示不变的值而不是状态可变的对象。 Strings, numbers, etc are immutable.字符串、数字等是不可变的。 At the end, usually our objects boil down to data structures eventually referencing immutable values, but we change the data structures by assigning new values to the same field names.最后,通常我们的对象归结为最终引用不可变值的数据结构,但我们通过将新值分配给相同的字段名称来更改数据结构。

So to make something truly immutable you need to make sure that final is used all the way down, until you reach every field reaching every value at the base of your composition tree.因此,要使某些东西真正不可变,您需要确保一直使用 final,直到到达每个字段,并达到组合树底部的每个值。 Otherwise something could change out from under your the object and it isn't really fully immutable.否则,某些东西可能会从您的对象下方发生变化,并且它并不是完全不可变的。

Your example is already immutable object, because fields in Student class can only set on instance initialization.您的示例已经是不可变对象,因为 Student 类中的字段只能在实例初始化时设置。

To make object immutable, You must do these steps:要使对象不可变,您必须执行以下步骤:

  1. Don't use any methods, which can change fields of your class.不要使用任何方法,它可以改变你的类的字段。 For example don't use Setters.例如,不要使用 Setter。
  2. Avoid to use public non-final fields.避免使用公共非最终字段。 If your fields is public then you must declare them as final and initialize them in constructor or directly in the declaration line.如果您的字段是公共的,那么您必须将它们声明为 final 并在构造函数中或直接在声明行中初始化它们。

It is too late to answer but may be it help other peoples who have this question.现在回答为时已晚,但可能会帮助其他有此问题的人。

  1. State of immutable object can not be modified after construction, any modification should result in new immutable object.不可变对象的状态在构造后不能修改,任何修改都应该产生新的不可变对象。
  2. All fields of Immutable class should be final. Immutable 类的所有字段都应该是最终的。
  3. Object must be properly constructed ie object reference must not leak during construction process.对象必须正确构造,即对象引用在构造过程中不能泄漏。
  4. Object should be final in order to restrict sub-class for altering immutability of parent class.为了限制子类改变父类的不变性,对象应该是最终的。

I think this link help more Read more: http://javarevisited.blogspot.com/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html#ixzz40VDQDDL1我认为这个链接帮助更多阅读更多: http : //javarevisited.blogspot.com/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html#ixzz40VDQDDL1

It already is immutable -- you can't change the contents once you initialize it, since you haven't made setters.它已经是不可变的——一旦你初始化它,你就不能改变它的内容,因为你还没有创建 setter。 You might add final keywords to the variables.您可以将 final 关键字添加到变量中。

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

您只需遵循本示例中显示的指南(谷歌中的第一个结果): http : //www.javapractices.com/topic/TopicAction.do? Id= 29

Here are few rules, which helps to make a class immutable in Java : 1. State of immutable object can not be modified after construction, any modification should result in new immutable object.这里有一些规则,这有助于在 Java 中使类不可变: 1. 不可变对象的状态在构造后不能修改,任何修改都应产生新的不可变对象。 2. All fields of Immutable class should be final. 2. Immutable 类的所有字段都应该是最终的。 3. Object must be properly constructed ie object reference must not leak during construction process. 3. 对象必须正确构造,即对象引用在构造过程中不能泄漏。 4. Object should be final in order to restrict sub-class for altering immutability of parent class. 4.对象应该是最终的,以限制子类改变父类的不变性。

Example:例子:

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

} }

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

According to Strategy for Defining Immutable Objects根据定义不可变对象的策略

  1. Don't provide " setter " methods — methods that modify fields or objects referred to by fields.不要提供“ setter ”方法——修改字段或字段引用的对象的方法。

  2. Make all fields final and private .将所有字段设为finalprivate

  3. Don't allow subclasses to override methods.不允许子类覆盖方法。 The simplest way to do this is to declare the class as final .最简单的方法是将类声明为final

    a. a. A more sophisticated approach is to make the constructor private and construct instances in factory methods.更复杂的方法是使constructor私有并在工厂方法中构造实例。

  4. If the instance fields include references to mutable objects, don't allow those objects to be changed:如果实例字段包含对可变对象的引用,则不允许更改这些对象:

    a. a. Don't provide methods that modify the mutable objects.不要提供修改可变对象的方法。

    b.Don't share references to the mutable objects.不要共享对可变对象的引用。 Never store references to external, mutable objects passed to the constructor;永远不要存储对传递给构造函数的外部可变对象的引用; if necessary, create copies, and store references to the copies.如有必要,创建副本并存储对副本的引用。 Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.同样,必要时创建内部可变对象的副本,以避免在方法中返回原始对象。

Java SE 16 Java SE 16

You can use JEP 395: Records feature, introduced as part of Java SE 16, to create an immutable class in a succinct manner.您可以使用JEP 395:记录功能(作为 Java SE 16 的一部分引入)以简洁的方式创建不可变类。

If you have already gone through the above link, you must have figured out that you can do it simply as如果你已经通过上面的链接,你一定已经想出你可以简单地做到这一点

record Student(String name, String age) { }

What you get in turn are:你依次得到的是:

  1. A final class Student . final class Student
  2. A canonical constructor whose signature is the same as the header, Student(String name, String age) .签名与标头Student(String name, String age)相同的规范构造函数。
  3. private final fields, name and age and their corresponding public accessor method with the same name and return type. private final字段、 nameage以及它们对应的具有相同名称和返回类型的public访问器方法。
  4. Automatically created equals , hashCode and toString methods.自动创建equalshashCodetoString方法。

Demo:演示:

Student.java学生.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()));
    }
}

Output:输出:

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

Make the class or variable as final that's more than enough将类或变量设为 final 就足够了

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

  // code
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM