为什么我们需要不可变类?

发布于 2024-09-24 15:10:55 字数 64 浏览 6 评论 0原文

我无法了解我们需要不可变类的场景。
你有遇到过这样的要求吗?或者您能给我们任何应该使用此模式的真实示例吗?

I am unable to get what are the scenarios where we need an immutable class.
Have you ever faced any such requirement? or can you please give us any real example where we should use this pattern.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

醉城メ夜风 2024-10-01 15:10:56

不可变类的一个尚未被提及的特性:存储对深度不可变类对象的引用是存储其中包含的所有状态的有效方法。假设我有一个可变对象,它使用深度不可变对象来保存 50K 的状态信息。进一步假设,我希望有 25 次“复制”我的原始(可变)对象(例如,用于“撤消”缓冲区);复制操作之间的状态可能会发生变化,但通常不会。制作可变对象的“副本”只需要复制对其不可变状态的引用,因此 20 个副本相当于 20 个引用。相比之下,如果状态保存在 50K 的可变对象中,则 25 个复制操作中的每一个都必须生成自己的 50K 数据的副本;保存所有 25 个副本将需要保存超过 1 兆的大部分重复数据。尽管第一个复制操作会生成永远不会改变的数据副本,并且理论上其他 24 个操作可以简单地引用该数据,但在大多数实现中,第二个对象无法请求该数据的副本。知道不可变副本已经存在的信息(*)。

(*) 有时有用的一种模式是可变对象有两个字段来保存其状态——一个采用可变形式,一个采用不可变形式。对象可以被复制为可变或不可变的,并且可以从一个或另一个引用集开始生命。一旦对象想要更改其状态,它就会将不可变引用复制到可变引用(如果尚未完成)并使不可变引用无效。当对象被复制为不可变对象时,如果未设置其不可变引用,则会创建一个不可变副本,并且不可变引用指向该副本。这种方法将比“完整的写入时复制”需要更多的复制操作(例如,要求复制一个已变异的对象,因为上次复制将需要复制操作,即使原始对象再也不会发生变异) )但它避免了 FFCOW 带来的线程复杂性。

One feature of immutable classes which hasn't yet been called out: storing a reference to a deeply-immutable class object is an efficient means of storing all of the state contained therein. Suppose I have a mutable object which uses a deeply-immutable object to hold 50K worth of state information. Suppose, further, that I wish to on 25 occasions make a "copy" of my original (mutable) object (e.g. for an "undo" buffer); the state could change between copy operations, but usually doesn't. Making a "copy" of the mutable object would simply require copying a reference to its immutable state, so 20 copies would simply amount to 20 references. By contrast, if the state were held in 50K worth of mutable objects, each of the 25 copy operations would have to produce its own copy of 50K worth of data; holding all 25 copies would require holding over a meg worth of mostly-duplicated data. Even though the first copy operation would produce a copy of the data that will never change, and the other 24 operations could in theory simply refer back to that, in most implementations there would be no way for the second object asking for a copy of the information to know that an immutable copy already exists(*).

(*) One pattern that can sometimes be useful is for mutable objects to have two fields to hold their state--one in mutable form and one in immutable form. Objects can be copied as mutable or immutable, and would begin life with one or the other reference set. As soon as the object wants to change its state, it copies the immutable reference to the mutable one (if it hasn't been done already) and invalidates the immutable one. When the object is copied as immutable, if its immutable reference isn't set, an immutable copy will be created and the immutable reference pointed to that. This approach will require a few more copy operations than would a "full-fledged copy on write" (e.g. asking to copy an object which has been mutated since the last copy would require a copy operation, even if the original object is never again mutated) but it avoids the threading complexities that FFCOW would entail.

浮华 2024-10-01 15:10:56

为什么是不可变类?

一旦对象被实例化,它的状态在生命周期内就不能改变。这也使得它线程安全。

示例:

显然是 String、Integer 和 BigDecimal 等。这些值一旦创建,在生命周期内就无法更改。

用例:
使用其配置值创建数据库连接对象后,您可能不需要更改其状态,您可以使用不可变类

Why Immutable class?

Once an object is instantiated it state cannot be changed in lifetime. Which also makes it thread safe.

Examples :

Obviously String, Integer and BigDecimal etc. Once these values are created cannot be changed in lifetime.

Use-case :
Once Database connection object is created with its configuration values you might not need to change its state where you can use an immutable class

神仙妹妹 2024-10-01 15:10:56

来自有效的 Java;
不可变类只是其实例无法修改的类。所有的
每个实例中包含的信息在创建时提供,并且是
在对象的生命周期内固定。 Java 平台库包含许多
不可变类,包括 String、装箱原始类和 BigInte-
ger 和 BigDecimal。这样做有很多充分的理由:
比可变类更容易设计、实现和使用。他们不太容易
错误并且更安全。

from Effective Java;
An immutable class is simply a class whose instances cannot be modified. All of
the information contained in each instance is provided when it is created and is
fixed for the lifetime of the object. The Java platform libraries contain many
immutable classes, including String, the boxed primitive classes, and BigInte-
ger and BigDecimal. There are many good reasons for this: Immutable classes
are easier to design, implement and use than mutable classes. They are less prone
to error and are more secure.

长亭外,古道边 2024-10-01 15:10:56

不可变类非常适合缓存目的,因为您不必担心值更改。不可变类的另一个好处是它本质上是线程安全的,因此在多线程环境中您不必担心线程安全。

An immutable class is good for caching purposes because you don't have to worry about the value changes. Another benefit of an immutable class is that it is inherently thread-safe, so you don't have to worry about thread safety in case of a multi-threaded environment.

像你 2024-10-01 15:10:55

其他答案似乎过于专注于解释为什么不变性是好的。它非常好,我只要有可能就使用它。 但是,这不是你的问题。我将逐点回答您的问题,以确保您获得所需的答案和示例。

我无法了解我们需要不可变类的场景。

这里的“需要”是一个相对术语。不可变类是一种设计模式,与任何范例/模式/工具一样,它可以使构建软件变得更容易。类似地,在 OO 范式出现之前就已经编写了很多代码,但算我一个“需要”OO 的程序员吧。不可变的类,比如面向对象,并不是严格意义上需要的,但我会表现得像我需要它们一样。

您曾经遇到过这样的要求吗?

如果您没有以正确的视角看待问题域中的对象,您可能看不到不可变对象的需求。如果您不熟悉何时有利地使用不可变类,可能很容易认为问题域不需要任何不可变类。

我经常使用不可变类,将问题域中的给定对象视为值或固定实例。这个概念有时取决于视角或观点,但理想情况下,很容易切换到正确的视角来识别好的候选对象。

通过确保阅读各种书籍/在线文章以更好地了解如何思考不可变类,您可以更好地了解不可变对象在哪里真正有用(如果不是绝对必要的话) 。一篇帮助您入门的好文章是 Java 理论与实践:是否要改变变异?

我将尝试在下面给出几个示例,说明人们如何以不同的视角(可变与不可变)看待对象,以阐明我所说的视角的含义。

...您能给我们任何应该使用此模式的真实示例吗?

既然你要求提供真实的例子,我会给你一些,但首先,让我们从一些经典的例子开始。

经典值对象

字符串和整数通常被视为值。因此,发现 String 类和 Integer 包装类(以及其他包装类)在 Java 中是不可变的就不足为奇了。颜色通常被认为是一个值,因此是不可变的 Color 类。

反例

相比之下,汽车通常不被视为价值对象。对汽车建模通常意味着创建一个具有变化状态(里程表、速度、燃油水平等)的类。然而,在某些领域它可能是一个价值对象。例如,一辆汽车(或者特别是汽车模型)可能被视为应用程序中的值对象,用于查找给定车辆的合适机油。

扑克牌

曾经编写过扑克牌程序吗?我做到了。我可以将一张扑克牌表示为具有可变花色和等级的可变对象。一手抽牌扑克牌可能有 5 个固定实例,其中替换我手中的第 5 张牌意味着通过更改其花色和等级 ivar 将第 5 个扑克牌实例突变为新牌。

然而,我倾向于将扑克牌视为一种不可变的物体,一旦创建,它就具有固定不变的花色和等级。我的抽牌扑克手牌将有 5 个实例,更换我手中的一张牌将涉及丢弃其中一个实例并向我的手牌添加一个新的随机实例。

地图投影

最后一个例子是,当我编写一些地图代码时,地图可以以各种 预测。原始代码让地图使用固定但可变的投影实例(如上面的可变扑克牌)。更改地图投影意味着改变地图投影实例的 ivars(投影类型、中心点、缩放等)。

然而,如果我将投影视为不可变值或固定实例,我觉得设计会更简单。更改地图投影意味着让地图引用不同的投影实例,而不是改变地图的固定投影实例。这也使得捕获命名投影(例如 MERCATOR_WORLD_VIEW)变得更加简单。

The other answers seem too focused on explaining why immutability is good. It is very good and I use it whenever possible. However, that is not your question. I'll take your question point by point to try to make sure you're getting the answers and examples you need.

I am unable to get what are the scenarios where we need an immutable class.

"Need" is a relative term here. Immutable classes are a design pattern that, like any paradigm/pattern/tool, is there to make constructing software easier. Similarly, plenty of code was written before the OO paradigm came along, but count me among the programmers that "need" OO. Immutable classes, like OO, aren't strictly needed, but I going to act like I need them.

Have you ever faced any such requirement?

If you aren't looking at the objects in the problem domain with the right perspective, you may not see a requirement for an immutable object. It might be easy to think that a problem domain doesn't require any immutable classes if you're not familiar when to use them advantageously.

I often use immutable classes where I think of a given object in my problem domain as a value or fixed instance. This notion is sometimes dependent on perspective or viewpoint, but ideally, it will be easy to switch into the right perspective to identify good candidate objects.

You can get a better sense of where immutable objects are really useful (if not strictly necessary) by making sure you read up on various books/online articles to develop a good sense of how to think about immutable classes. One good article to get you started is Java theory and practice: To mutate or not to mutate?

I'll try to give a couple of examples below of how one can see objects in different perspectives (mutable vs immutable) to clarify what I mean by perspective.

... can you please give us any real example where we should use this pattern.

Since you asked for real examples I'll give you some, but first, let's start with some classic examples.

Classic Value Objects

Strings and integers are often thought of as values. Therefore it's not surprising to find that String class and the Integer wrapper class (as well as the other wrapper classes) are immutable in Java. A color is usually thought of as a value, thus the immutable Color class.

Counterexample

In contrast, a car is not usually thought of as a value object. Modeling a car usually means creating a class that has changing state (odometer, speed, fuel level, etc). However, there are some domains where it car may be a value object. For example, a car (or specifically a car model) might be thought of as a value object in an app to look up the proper motor oil for a given vehicle.

Playing Cards

Ever write a playing card program? I did. I could have represented a playing card as a mutable object with a mutable suit and rank. A draw-poker hand could be 5 fixed instances where replacing the 5th card in my hand would mean mutating the 5th playing card instance into a new card by changing its suit and rank ivars.

However, I tend to think of a playing card as an immutable object that has a fixed unchanging suit and rank once created. My draw poker hand would be 5 instances and replacing a card in my hand would involve discarding one of those instance and adding a new random instance to my hand.

Map Projection

One last example is when I worked on some map code where the map could display itself in various projections. The original code had the map use a fixed, but mutatable projection instance (like the mutable playing card above). Changing the map projection meant mutating the map's projection instance's ivars (projection type, center point, zoom, etc).

However, I felt the design was simpler if I thought of a projection as an immutable value or fixed instance. Changing the map projection meant having the map reference a different projection instance rather than mutating the map's fixed projection instance. This also made it simpler to capture named projections such as MERCATOR_WORLD_VIEW.

甜是你 2024-10-01 15:10:55

一般来说,不可变类更容易正确设计、实现和使用。 String 就是一个例子:java.lang.String 的实现比 C++ 中的 std::string 简单得多,主要是因为它的不变性。

不可变性产生特别大差异的一个特定领域是并发性:不可变对象可以在多个线程之间安全地共享,而可变对象必须通过仔细的设计和实现来实现线程安全 - 通常这远非如此一项微不足道的任务。

更新: Effective Java 第二版 详细解决了这个问题 - 请参阅第 15 项:最小化可变性

另请参阅这些相关帖子:

Immutable classes are in general much simpler to design, implement and use correctly. An example is String: the implementation of java.lang.String is significantly simpler than that of std::string in C++, mostly due to its immutability.

One particular area where immutability makes an especially big difference is concurrency: immutable objects can safely be shared among multiple threads, whereas mutable objects must be made thread-safe via careful design and implementation - usually this is far from a trivial task.

Update: Effective Java 2nd Edition tackles this issue in detail - see Item 15: Minimize mutability.

See also these related posts:

—━☆沉默づ 2024-10-01 15:10:55

Joshua Bloch 的《Effective Java》概述了编写不可变类的几个原因:

  • 简单 - 每个类仅处于一种状态
  • 线程安全 - 因为状态无法更改,因此不需要同步
  • 以不可变风格编写可以生成更健壮的代码。想象一下,如果字符串不是不可变的;任何返回 String 的 getter 方法都需要实现在返回 String 之前创建一个防御性副本 - 否则客户端可能会意外或恶意地破坏对象的该状态。

一般来说,使对象不可变是一种很好的做法,除非导致严重的性能问题。在这种情况下,可变构建器对象可以用于构建不可变对象,例如 StringBuilder

Effective Java by Joshua Bloch outlines several reasons to write immutable classes:

  • Simplicity - each class is in one state only
  • Thread Safe - because the state cannot be changed, no synchronization is required
  • Writing in an immutable style can lead to more robust code. Imagine if Strings weren't immutable; Any getter methods that returned a String would require the implementation to create a defensive copy before the String was returned - otherwise a client may accidentally or maliciously break that state of the object.

In general it is good practise to make an object immutable unless there are severe performance problems as a result. In such circumstances, mutable builder objects can be used to build immutable objects e.g. StringBuilder

独自←快乐 2024-10-01 15:10:55

哈希图是一个典型的例子。地图的键必须是不可变的。如果键不是不可变的,并且您更改了键上的值,使得 hashCode() 会产生新值,则映射现在已损坏(键现在位于哈希表中的错误位置。)。

Hashmaps are a classic example. It's imperative that the key to a map be immutable. If the key is not immutable, and you change a value on the key such that hashCode() would result in a new value, the map is now broken (a key is now in the wrong location in the hash table.).

琉璃梦幻 2024-10-01 15:10:55

Java 实际上是一本参考资料。有时一个实例会被多次引用。如果您更改这样的实例,它将反映到其所有引用中。有时您只是不想用它来提高鲁棒性和线程安全性。然后,不可变类很有用,因此人们被迫创建一个新实例并将其重新分配给当前引用。这样,其他引用的原始实例就保持不变。

想象一下,如果 String 是可变的,Java 会是什么样子。

Java is practically one and all references. Sometimes an instance is referenced multiple times. If you change such an instance, it would be reflected into all its references. Sometimes you simply don't want to have this to improve robustness and threadsafety. Then an immutable class is useful so that one is forced to create a new instance and reassign it to the current reference. This way the original instance of the other references remain untouched.

Imagine how Java would look like if String was mutable.

蝶舞 2024-10-01 15:10:55

让我们看一个极端的情况:整数常量。如果我写一个像“x=x+1”这样的语句,我想 100% 确信数字“1”不会以某种方式变成 2,无论程序中其他地方发生什么。

好吧,整数常量不是一个类,但概念是相同的。假设我写:

String customerId=getCustomerId();
String customerName=getCustomerName(customerId);
String customerBalance=getCustomerBalance(customerid);

看起来很简单。但如果字符串不是不可变的,那么我就必须考虑 getCustomerName 可能更改 customerId 的可能性,这样当我调用 getCustomerBalance 时,我会获得不同客户的余额。现在您可能会说,“为什么有人会编写 getCustomerName 函数来更改 id?这毫无意义。”但这正是您可能遇到麻烦的地方。编写上述代码的人可能会认为函数不会更改参数是显而易见的。然后有人必须修改该函数的另一种用法来处理客户在同一名称下拥有多个帐户的情况。他说,“哦,这是这个方便的 getCustomer name 函数,它已经在查找名称了。我只需让它自动将 id 更改为具有相同名称的下一个帐户,并将其放入循环中......”并且然后你的程序开始神秘地无法工作。这会是糟糕的编码风格吗?大概。但在副作用不明显的情况下,这恰恰是一个问题。

不变性简单来说就是某类对象是常量,我们可以将它们视为常量。

(当然,用户可以将不同的“常量对象”分配给变量。有人可以写
字符串 s="你好";
然后稍后写
s=“再见”;
除非我将变量设为最终变量,否则我无法确定它不会在我自己的代码块中发生更改。就像整数常量向我保证“1”始终是相同的数字,但并不是“x=1”永远不会因为写入“x=2”而改变。但我可以确信,如果我有一个不可变对象的句柄,那么我传递给它的函数都不能更改它,或者如果我制作它的两个副本,则对保存一个副本的变量进行更改将不会改变另一个。 ETC。

Let's take an extreme case: integer constants. If I write a statement like "x=x+1" I want to be 100% confidant that the number "1" will not somehow become 2, no matter what happens anywhere else in the program.

Now okay, integer constants are not a class, but the concept is the same. Suppose I write:

String customerId=getCustomerId();
String customerName=getCustomerName(customerId);
String customerBalance=getCustomerBalance(customerid);

Looks simple enough. But if Strings were not immutable, then I would have to consider the possibility that getCustomerName could change customerId, so that when I call getCustomerBalance, I am getting the balance for a different customer. Now you might say, "Why in the world would someone writing a getCustomerName function make it change the id? That would make no sense." But that's exactly where you could get in trouble. The person writing the above code might take it as just obvious that the functions would not change the parameter. Then someone comes along who has to modify another use of that function to handle the case where where a customer has multiple accounts under the same name. And he says, "Oh, here's this handy getCustomer name function that's already looking up the name. I'll just make that automatically change the id to the next account with the same name, and put it in a loop ..." And then your program starts mysteriously not working. Would that be bad coding style? Probably. But it's precisely a problem in cases where the side effect is NOT obvious.

Immutability simply means that a certain class of objects are constants, and we can treat them as constants.

(Of course the user could assign a different "constant object" to a variable. Someone can write
String s="hello";
and then later write
s="goodbye";
Unless I make the variable final, I can't be sure that it's not being changed within my own block of code. Just like integer constants assure me that "1" is always the same number, but not that "x=1" will never be changed by writing "x=2". But I can be confidant that if I have a handle to an immutable object, that no function I pass it to can change it on me, or that if I make two copies of it, that a change to the variable holding one copy will not change the other. Etc.

朱染 2024-10-01 15:10:55

我们本身不需要不可变类,但它们肯定可以使某些编程任务变得更容易,尤其是在涉及多个线程时。您不必执行任何锁定即可访问不可变对象,并且您已经建立的有关此类对象的任何事实在将来仍然有效。

We don't need immutable classes, per se, but they can certainly make some programming tasks easier, especially when multiple threads are involved. You don't have to perform any locking to access an immutable object, and any facts that you've already established about such an object will continue to be true in the future.

君勿笑 2024-10-01 15:10:55

不可变性有多种原因:

  • 线程安全:不可变对象不能更改,其内部状态也不能更改,因此无需同步它。
  • 它还保证我通过(通过网络)发送的任何内容都必须处于与之前发送的状态相同的状态。这意味着没有人(窃听者)可以在我的不可变集中添加随机数据。
  • 开发起来也更简单。您保证如果对象是不可变的,则不会存在子类。例如 String 类。

因此,如果您想通过网络服务发送数据,并且希望保证结果与发送的结果完全相同,请将其设置为不可变。

There are various reason for immutability:

  • Thread Safety: Immutable objects cannot be changed nor can its internal state change, thus there's no need to synchronise it.
  • It also guarantees that whatever I send through (through a network) has to come in the same state as previously sent. It means that nobody (eavesdropper) can come and add random data in my immutable set.
  • It's also simpler to develop. You guarantee that no subclasses will exist if an object is immutable. E.g. a String class.

So, if you want to send data through a network service, and you want a sense of guarantee that you will have your result exactly the same as what you sent, set it as immutable.

掌心的温暖 2024-10-01 15:10:55

我对未来访客的 2 美分:

不可变对象是不错选择的 2 个场景是:

在多线程中

多线程环境中的并发问题可以通过同步很好地解决,但同步是昂贵的事情(不会在这里深究“为什么”),所以如果您使用不可变对象,那么就没有同步来解决并发问题,因为不可变对象的状态无法更改,如果状态无法更改,那么所有线程都可以无缝访问目的。 因此,不可变对象是多线程环境中共享对象的绝佳选择。

作为基于哈希的集合的键

使用基于哈希的集合时要注意的最重要的事情之一是该键应该使其 hashCode() 在对象的生命周期内始终返回相同的值,因为如果该值发生更改,则无法检索使用该对象进入基于哈希的集合的旧条目,因此会导致内存泄漏。 由于不可变对象的状态无法更改,因此它们是基于哈希的集合中作为键的绝佳选择。因此,如果您使用不可变对象作为基于哈希的集合的键,那么您可以确定会有因此不会出现任何内存泄漏(当然,当用作键的对象没有从其他任何地方引用时,仍然可能存在内存泄漏,但这不是这里的重点)。

My 2 cents for future visitors:

2 scenarios where immutable objects are good choices are:

In multi-threading

Concurrency issues in multi-threaded environment can very well be solved by synchronization but synchronization is costly affair (wouldn't dig here on "why"), so if you are using immutable objects then there is no synchronization to solve concurrency issue because state of immutable objects cannot be changed, and if state cannot be changed then all threads can seamless access the object. So, immutable objects makes a great choice for shared objects in multi-threaded environment.

As key for hash based collections

One of the most important thing to note when working with hash based collection is that key should be such that its hashCode() should always return the same value for the lifetime of the object, because if that value is changed then old entry made into the hash based collection using that object cannot be retrieved, hence it would cause memory leak. Since state of immutable objects cannot be changed so they makes a great choice as key in hash based collection. So, if you are using immutable object as key for hash based collection then you can be sure that there will not be any memory leak because of that (of course there can still be memory leak when the object used as key is not referenced from anywhere else, but that's not the point here).

梅窗月明清似水 2024-10-01 15:10:55

我将从不同的角度来攻击这个问题。我发现不可变对象让我在阅读代码时变得更轻松。

如果我有一个可变对象,如果它在我的直接范围之外使用过,我永远不确定它的值是什么。假设我在方法的局部变量中创建 MyMutableObject,用值填充它,然后将其传递给其他五个方法。这些方法中的任何一种都可以更改我的对象的状态,因此必须发生以下两件事之一:

  1. 我必须在考虑代码逻辑的同时跟踪五个附加方法的主体。
  2. 我必须为我的对象创建五个浪费的防御副本,以确保将正确的值传递给每个方法。

第一个使得推理我的代码变得困难。第二个使我的代码性能下降——无论如何,我基本上都是在模仿一个具有写时复制语义的不可变对象,但无论被调用的方法是否实际上修改了我的对象的状态,我都会一直这样做。

如果我改用 MyImmutableObject,我可以确信我设置的值就是我的方法生命周期中的值。没有“幽灵般的远距离动作”可以从我的下方改变它,并且我不需要在调用其他五种方法之前为我的对象制作防御性副本。如果其他方法想要为了他们的目的改变东西他们必须制作副本 - 但他们只有在他们确实必须制作副本时才会这样做(而不是我在每个外部之前都这样做)方法调用)。我节省了自己的脑力资源来跟踪甚至可能不在当前源文件中的方法,并且节省了系统的开销,以防止万一无休止地制作不必要的防御性副本。

(如果我走出 Java 世界,进入 C++ 世界等,我会变得更加棘手。我可以使对象看起来好像是可变的,但在幕后使它们透明地克隆到任何对象上)一种状态改变——即写时复制——没有人更明智。)

I'm going to attack this from a different perspective. I find immutable objects make life easier for me when reading code.

If I have a mutable object I am never sure what its value is if it's ever used outside of my immediate scope. Let's say I create MyMutableObject in a method's local variables, fill it out with values, then pass it to five other methods. ANY ONE of those methods can change my object's state, so one of two things has to occur:

  1. I have to keep track of the bodies of five additional methods while thinking about my code's logic.
  2. I have to make five wasteful defensive copies of my object to ensure that the right values get passed to each method.

The first makes reasoning about my code difficult. The second makes my code suck in performance -- I'm basically mimicking an immutable object with copy-on-write semantics anyway, but doing it all the time whether or not the called methods actually modify my object's state.

If I instead use MyImmutableObject, I can be assured that what I set is what the values will be for the life of my method. There's no "spooky action at a distance" that will change it out from under me and there's no need for me to make defensive copies of my object before invoking the five other methods. If the other methods want to change things for their purposes they have to make the copy – but they only do this if they really have to make a copy (as opposed to my doing it before each and every external method call). I spare myself the mental resources of keeping track of methods which may not even be in my current source file, and I spare the system the overhead of endlessly making unnecessary defensive copies just in case.

(If I go outside of the Java world and into, say, the C++ world, among others, I can get even trickier. I can make the objects appear as if they're mutable, but behind the scenes make them transparently clone on any kind of state change—that's copy-on-write—with nobody being the wiser.)

风为裳 2024-10-01 15:10:55

不可变对象是其状态一旦启动就不会改变的实例。
此类对象的使用是特定要求的。

不可变类有利于缓存目的并且是线程安全的。

Immutable objects are instances whose states do not change once initiated.
The use of such objects is requirement specific.

Immutable class is good for caching purpose and it is thread safe.

白馒头 2024-10-01 15:10:55

凭借不变性,您可以确保底层不可变对象的行为/状态不会改变,这样您就可以获得执行其他操作的额外优势:

  • 您可以使用多个核心/处理(并发/并行处理)轻松(因为操作顺序不再重要。)

  • 可以对昂贵的操作进行缓存(因为您确信相同
    结果)。

  • 可以轻松进行调试(因为运行历史不会成为问题
    不再)

By the virtue of immutability you can be sure that the behavior/state of the underlying immutable object do not to change, with that you get added advantage of performing additional operations:

  • You can use multiple core/processing(concurrent/parallel processing) with ease(as the sequence of operations will no longer matter.)

  • Can do caching for expensive operations (as you are sure of the same
    result).

  • Can do debugging with ease(as the history of run will not be a concern
    anymore)

凉世弥音 2024-10-01 15:10:55

使用final关键字并不一定会使某些东西变得不可变:

public class Scratchpad {
    public static void main(String[] args) throws Exception {
        SomeData sd = new SomeData("foo");
        System.out.println(sd.data); //prints "foo"
        voodoo(sd, "data", "bar");
        System.out.println(sd.data); //prints "bar"
    }

    private static void voodoo(Object obj, String fieldName, Object value) throws Exception {
        Field f = SomeData.class.getDeclaredField("data");
        f.setAccessible(true);
        Field modifiers = Field.class.getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL);
        f.set(obj, "bar");
    }
}

class SomeData {
    final String data;
    SomeData(String data) {
        this.data = data;
    }
}

只是一个例子来证明“final”关键字是为了防止程序员犯错,仅此而已。虽然重新分配缺少 Final 关键字的值很容易意外发生,但要更改值必须有意地达到这个长度。它用于文档并防止程序员错误。

Using the final keyword doesn't necessarily make something immutable:

public class Scratchpad {
    public static void main(String[] args) throws Exception {
        SomeData sd = new SomeData("foo");
        System.out.println(sd.data); //prints "foo"
        voodoo(sd, "data", "bar");
        System.out.println(sd.data); //prints "bar"
    }

    private static void voodoo(Object obj, String fieldName, Object value) throws Exception {
        Field f = SomeData.class.getDeclaredField("data");
        f.setAccessible(true);
        Field modifiers = Field.class.getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL);
        f.set(obj, "bar");
    }
}

class SomeData {
    final String data;
    SomeData(String data) {
        this.data = data;
    }
}

Just an example to demonstrate that the "final" keyword is there to prevent programmer error, and not much more. Whereas reassigning a value lacking a final keyword can easily happen by accident, going to this length to change a value would have to be done intentionally. It's there for documentation and to prevent programmer error.

葮薆情 2024-10-01 15:10:55

不可变数据结构在编写递归算法时也很有帮助。例如,假设您正在尝试解决 3SAT 问题。一种方法是执行以下操作:

  • 选择一个未分配的变量。
  • 为其指定 TRUE 值。通过取出现在满足的子句来简化实例,然后递归求解更简单的实例。
  • 如果 TRUE 情况下的递归失败,则将该变量分配为 FALSE。简化这个新实例,然后递归求解。

如果您有一个可变结构来表示问题,那么当您简化 TRUE 分支中的实例时,您必须:

  • 跟踪您所做的所有更改,并在您意识到问题无法解决时撤消所有更改解决了。这会产生很大的开销,因为您的递归可能会非常深入,并且编码起来很棘手。
  • 制作实例的副本,然后修改该副本。这会很慢,因为如果您的递归深度有几十级,您将必须制作该实例的许多副本。

但是,如果您以巧妙的方式对其进行编码,则可以拥有不可变的结构,其中任何操作都会返回问题的更新(但仍然不可变)版本(类似于 String.replace - 它不替换字符串,只是给你一个新的)。实现这一点的天真的方法是让“不可变”结构只需复制并在任何修改时创建一个新的结构,当具有可变结构时将其减少为第二个解决方案,并具有所有这些开销,但您可以用更多种方式来实现有效的方式。

Immutable data structures can also help when coding recursive algorithms. For example, say that you're trying to solve a 3SAT problem. One way is to do the following:

  • Pick an unassigned variable.
  • Give it the value of TRUE. Simplify the instance by taking out clauses that are now satisfied, and recur to solve the simpler instance.
  • If the recursion on the TRUE case failed, then assign that variable FALSE instead. Simplify this new instance, and recur to solve it.

If you have a mutable structure to represent the problem, then when you simplify the instance in the TRUE branch, you'll either have to:

  • Keep track of all changes you make, and undo them all once you realize the problem can't be solved. This has large overhead because your recursion can go pretty deep, and it's tricky to code.
  • Make a copy of the instance, and then modify the copy. This will be slow because if your recursion is a few dozen levels deep, you'll have to make many many copies of the instance.

However if you code it in a clever way, you can have an immutable structure, where any operation returns an updated (but still immutable) version of the problem (similar to String.replace - it doesn't replace the string, just gives you a new one). The naive way to implement this is to have the "immutable" structure just copy and make a new one on any modification, reducing it to the 2nd solution when having a mutable one, with all that overhead, but you can do it in a more efficient way.

独自←快乐 2024-10-01 15:10:55

“需要”不可变类的原因之一是通过引用传递所有内容并且不支持对象的只读视图(即 C++ 的 const)。

考虑一个支持观察者模式的类的简单情况:

class Person {
    public string getName() { ... }
    public void registerForNameChange(NameChangedObserver o) { ... }
}

如果 string 不是不可变的,则 Person 类就不可能实现 registerForNameChange() 正确,因为有人可以编写以下内容,有效地修改该人的姓名而不触发任何通知。

void foo(Person p) {
    p.getName().prepend("Mr. ");
}

在 C++ 中,getName() 返回 const std::string& 具有按引用返回并阻止访问变元的效果,这意味着在该上下文中不需要不可变类。

One of the reasons for the "need" for immutable classes is the combination of passing everything by reference and having no support for read-only views of an object (i.e. C++'s const).

Consider the simple case of a class having support for the observer pattern:

class Person {
    public string getName() { ... }
    public void registerForNameChange(NameChangedObserver o) { ... }
}

If string were not immutable, it would be impossible for the Person class to implement registerForNameChange() correctly, because someone could write the following, effectively modifying the person's name without triggering any notification.

void foo(Person p) {
    p.getName().prepend("Mr. ");
}

In C++, getName() returning a const std::string& has the effect of returning by reference and preventing access to mutators, meaning immutable classes are not necessary in that context.

月隐月明月朦胧 2024-10-01 15:10:55

他们也给了我们保证。不变性的保证意味着我们可以扩展它们并创建新的模式以提高效率,这是其他方式不可能实现的。

http://en.wikipedia.org/wiki/Singleton_pattern

They also give us a guarantee. The guarantee of immutability means that we can expand on them and create new patters for efficiency that are otherwise not possible.

http://en.wikipedia.org/wiki/Singleton_pattern

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文