简体   繁体   English

不变的类不是那么不变

[英]Immutable classes not so immutable

i read this , and though one of the answers makes sense, I still feel there's something wrong, so I'll go on and give an example. 我读了这篇文章 ,尽管答案之一很有意义,但我仍然觉得出了点问题,所以我继续举例。

suppose you have this class: 假设您有此类:

public class ClassA {
  private List<E> mList;

  //Constructor assigns some value to mList...

  public List<E> getList() {
    return mList;
  }
}

This class is said to be immutable , and yet, when I do something like this in another class, say ClassB, stuff happens: 据说该类是不可变的 ,但是,当我在另一个类(例如ClassB)中执行此类操作时,会发生以下情况:

public class ClassB {

  //variables and constructors....

  public void aFunction(ClassA classAObject) {
    List<E> anotherList = classAObject.getList();

    //play around with the variable anotherList, and though ClassA is immutable, I can change the value of its mList variable.
  }

}

Now I know that I could add that 'final', as mentioned in the link i posted above, but seriously, am I missing something regarding this immutable concept? 现在我知道我可以添加“ final”(如我在上面发布的链接中所述),但是说真的,我是否缺少有关此不变概念的内容? I mean, is there some reason why it really isn't that immutable? 我的意思是,出于某种原因,它确实不是那么一成不变吗?

The "final" attribute affects only the reference to the list, not the list itself. “最终”属性仅影响对列表的引用,而不影响列表本身。 If you want an immutable list, the List object itself has to be immutable. 如果要一个不可变的列表,则List对象本身必须是不可变的。 Thankfully, there are some standard solutions for this: 幸运的是,有一些标准的解决方案:

You can't change the value of mList , that is, assign a different value to that property. 您不能更改mList的值, mList该属性分配其他值。 But you can change the contents of mList . 但是您可以更改mList的内容。
Return Collections.unmodifiableList(this.mList) if you want to avoid this. 如果要避免这种情况,请返回Collections.unmodifiableList(this.mList)

Check "Effective Java" by Joshua Bloch (specifically "Minimize mutability"). 选中Joshua Bloch撰写的“ Effective Java”(特别是“最小化可变性”)。

First of all, ClassA is not immutable, nor List contained in it. 首先,ClassA不是不可变的,也不是包含List的。 Immutability for reference types is not possible through "final" definition unless provided within the object as its functionality. 除非在对象中提供引用功能,否则无法通过“最终”定义实现引用类型的不变性。 In the given example, List can not be immutable because it doesn't designed to be. 在给定的示例中,List不能是不可变的,因为它不是设计成这样的。 You may use: 您可以使用:

  public List<E> getList() {
    return Collections.unmodifiableList(list);
  }

or 要么

  public E[] getList() {
    return (E[]) list.toArray();
  }

for making list objects -practially, not truly- immutable to prevent changes from outside. 实际上,不是真正可变地制作列表对象,以防止外部更改。

As a general advice, returning collection types as a reference is not a good practice in terms of encapsulation and OOP principles. 作为一般建议,就封装和OOP原则而言,返回集合类型作为参考不是一个好习惯。 In this way you: 通过这种方式,您可以:

  1. open the internals of your object to outside world, 打开对象内部的外部世界,
  2. enable possibly unintended changes (or have to think about and prevent them) 启用可能意外的更改(或必须考虑并防止更改)
  3. after a certain amount of time (means the code is being called from more and more places) you make your class very difficult to change and adopt future requirements. 在一定时间后(意味着从越来越多的地方调用代码),您将很难改变类并采用将来的要求。

That's why you have to abstract the internal parts of the object from the outside world and open only required parts. 这就是为什么您必须从外部抽象对象的内部部分并仅打开必需的部分的原因。 In general, immutability for reference types is more about their abstraction and encapsulation, not to be provided extrinsically. 通常,引用类型的不变性更多地是关于它们的抽象和封装,而不是外部提供。

The class is considered immutable when all its member fields are immutable, both scalar fields and references , but not necessary referenced objects. 当该类的所有成员字段都是不变的时,则该类被视为不可变的,标量字段和引用都是必需的,但不是必需的引用对象。 And list is such referenced, certainly non-trivial, object. 列表就是这样引用的对象,肯定是不平凡的。

To make immutable also referenced objects, like list you referring, it would require a bit more effort. 为了使不可变的也被引用的对象(如您引用的列表)更复杂。

If you want to extent to the referenced list, to start with put the final keyword to mList declaration, to make the reference safely immutable. 如果要扩展到引用的列表,请首先将final关键字mList声明中,以使引用安全地不可变。 OK, now let's secure the list. 好的,现在让我们保护列表。 You can: 您可以:

  • Do not expose mList at all, perhaps only ClassA.get(index) delegating to mList.get() would be enough for your needs. 根本不要公开mList ,也许仅委托给mList.get() ClassA.get(index) mList.get()就足够了。 You have to then ensure that list is filled in constructor and there is no other modifying access to mList . 然后,您必须确保在构造函数中填充了列表,并且没有其他修改对mList访问。
  • Create a defensive copy of that list and that you can safely expose 创建该列表的防御副本,您可以安全地暴露该列表
  • Create immutable wrapper adapter around the list, using for example: Collections.unmodifiableList(list) 使用以下示例在列表周围创建不可变的包装适配器: Collections.unmodifiableList(list)

But don't forget that the list again is just a collection of references and you again protected only the references, it guarantees nothing for the referenced objects from the list, the list items. 但是请不要忘记,列表再次只是引用的集合,您又只保护了引用,它不保证列表中的引用对象(列表项)没有任何内容。

To make immutable the whole object graph, if it is what you probably want, that would take even more effort. 要使整个对象图不可变,如果这是您可能想要的,则将花费更多的精力。 Immutability dos not necessarily come cheap, in terms of development effort and CPU, like to make defensive copies. 就开发工作量和CPU而言,不变性并不一定要便宜,就像制作防御性副本一样。

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

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