简体   繁体   English

如何在java中创建不可变类

[英]How to create immutable class in java

How can create immutable class in java.如何在java中创建不可变类。 if Student class has a relationship(address) how to create immutable class.如果学生类有关系(地址)如何创建不可变类。 I want to make the class below immutable我想让下面的类不可变

  final public class Student {
        private final Address add;
            private final int sid;
            public Student(int sid, String name, Address add) {
                super();
                this.sid = sid;
                this.name = name;
                this.add = add;
            }
            private final String name;
            public int getSid() {
                return sid;
            }
            public final String getName() {
                return name;
            }
            @Override
            public String toString() {
                return "Student [add=" + add + ", name=" + name + ", sid=" + sid + "]";
            }
            public Address getAdd() {
                return add;
            }


        }

        //I want to make the class below immutable
        public class Address {
            public int getAid() {
                return aid;
            }
            public String getStreet() {
                return street;
            }
            @Override
            public String toString() {
                return "Address [aid=" + aid + ", street=" + street + "]";
            }
            int aid;
            String street;
            public Address(int aid, String street) {
                super();
                this.aid = aid;
                this.street = street;
            }

        }


        public class First {
        public static void main(String[] args) {
            Address myAdd=new Address(179,"Maihill");
            Student st=new Student(99,"anoj",myAdd);
            System.out.println(st.toString());
            myAdd.aid=2376;
            System.out.println(st);
            System.out.println("***************");
            Address pAdd=st.getAdd();
            //Here modified address instance then how we can make immutable.
                pAdd.aid=788;
            System.out.println(st);

        }
        }

Here we can modifiy address instances.在这里我们可以修改地址实例。 Please give me idea请给我主意

The key points for immutable are:不可变的关键点是:

  • no setters methods没有 setter 方法
  • make variables private and final使变量私有和最终
  • return lists using Collections.unmodifiableList - never return any mutable field;使用 Collections.unmodifiableList 返回列表 - 从不返回任何可变字段; always return either a copy (deep if appropriate) or an immutable version of the field始终返回该字段的副本(如果合适,则为深度)或不可变版本
  • make class final使班级决赛
  • if variables are changed internally in the class this change is not visible and has no effect outside of the class (including affecting things like equals() and hashcode() ).如果变量在类内部更改,则此更改不可见,并且在类外部没有影响(包括影响诸如equals()hashcode()之类的东西)。

In your class Address you should make your fields private (should) and final (must), like this -在您的类Address您应该将您的字段设为private (应该)和final (必须),如下所示 -

public final class Address {       // so no sub-classes can be made.
  private final int aid;           // private and final.
  private final String street;     // private and final.
  // as before.
}

You also cannot have setter methods, but when the fields are final that is not much of a problem (since any setter method would yield a compiler error).你也不能有 setter 方法,但是当字段是 final 时,这不是什么大问题(因为任何 setter 方法都会产生编译器错误)。

Well you made Student semi-immutable well:好吧,您使 Student 半不可变很好:

  • Its attributes are final它的属性是最终的
  • they are of immutable types (except the address)它们是不可变类型(地址除外)
  • and they are initialized in the constructor.它们在构造函数中初始化。

You shall apply the same thing to Address class, so it become immutable, and then all state of Student will be immutable.你应该把同样的东西应用到Address类,所以它变成不可变的,然后Student所有状态都将是不可变的。 So it will be:所以它将是:

public final class Address {
    private final int aid;
    private final String street;

    public Address(int aid, String street) {
        this.aid = aid;
        this.street = street;
    }


    public int getAid() {
        return aid;
    }

    public String getStreet() {
        return street;
    }

    ....
}

Fortunately you don't have any modifiable types (some of the most well-knowns are Date , Collection s and Maps ), otherwise you shall consider them too.幸运的是,您没有任何可修改的类型(一些最著名的是DateCollectionMaps ),否则您也应该考虑它们。

If you had any mutable attributes, you shall copy protect it in constructor, and you shall return a unmodifiable or a copy of it where state leaks.如果你有任何可变属性,你应该在构造函数中复制保护它,并且你应该在状态泄漏时返回一个不可修改的或它的副本。

For example if your Student class had a birthDate attribute, you shall do something like:例如,如果您的Student类有一个birthDate属性,您应该执行以下操作:

public final class Student {
    private final Date birthDate;

    public Student(int sid, String name, Address address, Date birthDate) {
        this.sid = sid;
        this.name = name;
        this.address = address;
        this.birthDate = (birthDate == null) ? null : new Date(birthDate.getTime());
    }

    public Date getBirthDate() {
       return (birthDate == null) ? null : new Date(birthDate.getTime());
    }

    ....

}

That's enough.就够了。 final declared can't be mutated and since there are required in the constructor as arguments, getter are redundant. final声明不能改变,因为构造函数中需要作为参数,getter 是多余的。

final public class Student {

    public final Address add;
    public final int sid;
    public final String name;

    public Student(int sid, String name, Address add) {
        super();
        this.sid = sid;
        this.name = name;
        this.add = add;
    }

    @Override
    public String toString() {
        return "Student [add=" + add + ", name=" + name + ", sid=" + sid + "]";
    }
}

address and studentId/id would be better names for the fields though. addressstudentId/id将是更好的字段名称。

To make a class immutable, follow these five rules:要使类不可变,请遵循以下五个规则:

from Effective Java - Third Edition - Chapter 4来自 Effective Java - 第三版 - 第 4 章

  1. Don't provide methods that modify the object's state (known as mutators) .不要提供修改对象状态的方法(称为 mutators)
  2. Ensure that the class can't be extended.确保该类无法扩展。 This prevents careless or malicious subclasses from compromising the immutable behavior of the class by behaving as if the object's state has changed.这可以防止粗心或恶意的子类通过表现得好像对象的状态已经改变来破坏类的不可变行为。 Preventing subclassing is generally accomplished by making the class final or provide a private constructor防止子类化通常通过使类成为 final提供私有构造函数来完成
  3. Make all fields final.使所有字段最终。 This clearly expresses your intent in a manner that is enforced by the system.这以系统强制执行的方式清楚地表达了您的意图。 Also, it is necessary to ensure correct behavior if a reference to a newly created instance is passed from one thread to another without synchronization, as spelled out in the memory model此外,如果对新创建的实例的引用在没有同步的情况下从一个线程传递到另一个线程,则有必要确保正确的行为,如内存模型中所述
  4. Make all fields private.所有字段设为私有。 This prevents clients from obtaining access to mutable objects referred to by fields and modifying these objects directly.这可以防止客户端访问由字段引用的可变对象并直接修改这些对象。 While it is technically permissible for immutable classes to have public final fields containing primitive values or references to immutable objects, it is not recommended because it precludes changing the internal representation in a later release.虽然技术上允许不可变类具有包含原始值或对不可变对象的引用的公共 final 字段,但不建议这样做,因为它阻止在以后的版本中更改内部表示。
  5. Ensure exclusive access to any mutable components.确保对任何可变组件的独占访问。 If your class has any fields that refer to mutable objects, ensure that clients of the class cannot obtain references to these objects.如果您的类有任何引用可变对象的字段,请确保该类的客户端无法获取对这些对象的引用。 Never initialize such a field to a client-provided object reference or return the field from an accessor.永远不要将这样的字段初始化为客户端提供的对象引用或从访问器返回该字段。 Make defensive copies (Item 50) in constructors, accessors, and readObject methods.在构造函数、访问器和readObject方法中制作防御性副本(条目 50)。

Complex.java - Example Immutable Class Complex.java - 示例不可变类

public final class Complex {
    private final double re;
    private final double im;

    public Complex(double re, double im) {
        this.re = re;
        this.im = im;

    }

    public double realPart() {
        return re;
    }

    public double imaginaryPart() {
        return im;
    }

    public Complex plus(Complex c) {
        return new Complex(re + c.re, im + c.im);

    }

    public Complex minus(Complex c) {
        return new Complex(re - c.re, im - c.im);
    }

    public Complex times(Complex c) {
        return new Complex(re * c.re - im * c.im,
                re * c.im + im * c.re);
    }

    public Complex dividedBy(Complex c) {

        double tmp = c.re * c.re + c.im * c.im;
        return new Complex((re * c.re + im * c.im) / tmp,
                (im * c.re - re * c.im) / tmp);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Complex))
            return false;

        Complex c = (Complex) o;
        // See page 47 to find out why we use compare instead of ==
        return Double.compare(c.re, re) == 0
                && Double.compare(c.im, im) == 0;
    }

    @Override
    public int hashCode() {
        return 31 * Double.hashCode(re) + Double.hashCode(im);

    }

    @Override
    public String toString() {
        return "(" + re + " + " + im + "i)";
    }
}

record

As of Java 16, we can use the records feature (also previewed in Java 14 , and previewed in Java 15 ).从 Java 16 开始,我们可以使用记录功能(也在Java 14 中预览,在 Java 15 中预览)。 Using record is the simplest, hassle-free way of creating Immutable class.使用record是创建不可变类的最简单、轻松的方法。

A record class is a shallowly immutable , transparent carrier for a fixed set of fields known as the record components that provides a state description for the record.记录类是一种浅不可变、透明的载体,用于一组固定的字段,称为记录components ,为记录提供state描述。 Each component gives rise to a final field that holds the provided value and an accessor method to retrieve the value.每个component都会产生一个保存所提供值的final字段和一个检索该值的accessor方法。 The field name and the accessor name match the name of the component.字段名称和访问器名称与组件名称匹配。

Let consider the example of creating an immutable rectangle让我们考虑创建一个不可变矩形的例子

record Rectangle(double length, double width) {}

No need to declare any constructor, no need to implement equals & hashCode methods.无需声明任何构造函数,无需实现equalshashCode方法。 Just any Records need a name and a state description.任何记录都需要名称和状态描述。

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

If you want to validate the value during object creation, we have to explicitly declare the constructor.如果要在对象创建期间验证值,我们必须显式声明构造函数。

public Rectangle {

    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

The record's body may declare static methods, static fields, static initializers, constructors, instance methods, and nested types.记录的主体可以声明静态方法、静态字段、静态初始值设定项、构造函数、实例方法和嵌套类型。

Instance Methods实例方法

record Rectangle(double length, double width) {
  
  public double area() {
    return this.length * this.width;
  }
}

static fields, methods静态字段、方法

Since state should be part of the components we cannot add instance fields to records.由于状态应该是组件的一部分,我们不能向记录添加实例字段。 But, we can add static fields and methods:但是,我们可以添加静态字段和方法:

record Rectangle(double length, double width) {
  
  static double aStaticField;
 
  static void printRectanglesIntersect(Rectangle rectangleA, Rectangle rectangleB) {
    System.out.println("Checking Rectangle intersection..");
  }
}

First we need to discussed what is immutable in java.首先我们需要讨论一下java中什么是不可变的。
In Java immutable means you state does not change once it has been initialize.在 Java 中不可变意味着您的状态一旦被初始化就不会改变。 the best example of immutable class is String.不可变类的最佳示例是 String。

We can also create our own immutable class, you have to do following steps.我们也可以创建我们自己的不可变类,您必须执行以下步骤。

  • Declare the Class as a final:将类声明为 final:

     Why? : As per the java final class can not be extended.
  • Declare all the fields as a private.将所有字段声明为私有字段。

     Why? : Because private member have not direct access out side of the class
  • Don't provide the setter method for that private field不要为该私有字段提供 setter 方法

    Why? : If you provide the setter method for the private members so you can access it out side of the class.
  • Make all fields as final.将所有字段设为最终字段。

     Why?: As per the java final variable can be assigned only once.
  • Initialize all the fields via constructor using deep copy.使用深拷贝通过构造函数初始化所有字段。

     import java.util.HashMap; import java.util.Iterator; public final class ImmutableClassExample { private final int id; private final String name; private final HashMap<String,String> testMap; public int getId() { return id; } public String getName() { return name; } /** * Accessor function for mutable objects */ public HashMap<String, String> getTestMap() { //return testMap; return (HashMap<String, String>) testMap.clone(); } /** * Constructor performing Deep Copy * @param i * @param n * @param hm */ public ImmutableClassExample(int i, String n, HashMap<String,String> hm){ System.out.println("Performing Deep Copy for Object initialization"); this.id=i; this.name=n; HashMap<String,String> tempMap=new HashMap<String,String>(); String key; Iterator<String> it = hm.keySet().iterator(); while(it.hasNext()){ key=it.next(); tempMap.put(key, hm.get(key)); } this.testMap=tempMap; } /** * Constructor performing Shallow Copy * @param i * @param n * @param hm */ /** public ImmutableClassExample(int i, String n, HashMap<String,String> hm){ System.out.println("Performing Shallow Copy for Object initialization"); this.id=i; this.name=n; this.testMap=hm; } */ /** * To test the consequences of Shallow Copy and how to avoid it with Deep Copy for creating immutable classes * @param args */ public static void main(String[] args) { HashMap<String, String> h1 = new HashMap<String,String>(); h1.put("1", "first"); h1.put("2", "second"); String s = "original"; int i=10; ImmutableClassExample ce = new ImmutableClassExample(i,s,h1); //Lets see whether its copy by field or reference System.out.println(s==ce.getName()); System.out.println(h1 == ce.getTestMap()); //print the ce values System.out.println("ce id:"+ce.getId()); System.out.println("ce name:"+ce.getName()); System.out.println("ce testMap:"+ce.getTestMap()); //change the local variable values i=20; s="modified"; h1.put("3", "third"); //print the values again System.out.println("ce id after local variable change:"+ce.getId()); System.out.println("ce name after local variable change:"+ce.getName()); System.out.println("ce testMap after local variable change:"+ce.getTestMap()); HashMap<String, String> hmTest = ce.getTestMap(); hmTest.put("4", "new"); System.out.println("ce testMap after changing variable from accessor methods:"+ce.getTestMap()); } }

you could use the lombok @Value annotation which creates an immutable class ie.你可以使用龙目岛@Value它创建一个不可变类,即注释。 it makes all fields private and final and makes the class itself final as well.它使所有字段都是privatefinal ,并使类本身也是final used collections are also immutable:使用的集合也是不可变的:

@Value
@Builder
public class Immutable {

    private String str;
    private int value;
    private List<String> strings;

}

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

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