为什么 Java 包装类是不可变的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12370544/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 08:37:13  来源:igfitidea点击:

Why are Java wrapper classes immutable?

javaimmutabilitymutableprimitive-types

提问by shrini1000

I know the usual reasons that apply to general immutable classes, viz

我知道适用于一般不可变类的通常原因,即

  1. can not change as a side effect
  2. easy to reason about their state
  3. inherently thread safe
  4. no need to provide clone/copy constructor/factory copy method
  5. instance caching
  6. no need for defensive copies.
  1. 不能作为副作用改变
  2. 很容易推理他们的状态
  3. 本质上线程安全
  4. 无需提供克隆/复制构造函数/工厂复制方法
  5. 实例缓存
  6. 不需要防御副本。

However, wrapper classes represent primitive types, and primitive types are mutable. So why aren't wrapper classes mutable?

然而,包装类代表原始类型,而原始类型是可变的。那么为什么包装类不可变呢?

回答by Jon Skeet

However, wrapper classes represent primitive types, and primitive types (except String) are mutable.

然而,包装类代表原始类型,原始类型(String 除外)是可变的。

Firstly, String isn't a primitive type.

首先,String 不是原始类型。

Secondly, it makes no sense to talk about the primitive types being mutable. If you change the value of a variablelike this:

其次,谈论原始类型是可变的是没有意义的。如果您像这样更改变量的值:

int x = 5;
x = 6;

That's not changing the number 5 - it's changing the value of x.

这并没有改变数字 5 - 它改变了x.

While the wrapper types could have been made mutable, it would have been annoying to do so, in my view. I frequently use readonly collections of these types, and wouldn't want them to be changeable. Very occasionally I want a mutable equivalent, but in that case it's easy enough to come up with one, or use the Atomic*classes.

虽然包装器类型可以设置为可变的,但在我看来这样做会很烦人。我经常使用这些类型的只读集合,并且不希望它们是可变的。偶尔我想要一个可变的等价物,但在这种情况下,想出一个或使用Atomic*类很容易。

I find myself wishing that Dateand Calendarwere immutable far more often than I find myself wanting Integerto be mutable... (Of course I normally reach for Joda Time instead, but one of the benefits of Joda Time isimmutability.)

我发现自己希望这样Date并且Calendar不可变的频率远远超过我发现自己想要Integer可变的次数......(当然,我通常会使用 Joda Time,但 Joda Time 的好处之一不变性。)

回答by Peter Lawrey

There are mutable, thread safe wrappers as well for some types.

对于某些类型,还有可变的、线程安全的包装器。

AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicLong
AtomicLongArray
AtomicReference - can wrap a String.
AtomicReferenceArray

Plus some exotic wrappers

加上一些异国情调的包装纸

AtomicMarkableReference - A reference and boolean
AtomicStampedReference - A reference and int

回答by Sean Patrick Floyd

For your info: if you want mutable holder classes, you can use the Atomic* classes in the java.util.concurrentpackage, e.g. AtomicInteger, AtomicLong

供您参考:如果您想要可变的持有者类,您可以使用java.util.concurrent包中的 Atomic* 类,例如AtomicIntegerAtomicLong

回答by Philipp

Here is an example where it would be quite bad when Integer would be mutable

这是一个示例,当 Integer 可变时会很糟糕

class Foo{
    private Integer value;
    public set(Integer value) { this.value = value; }
}

/* ... */

Foo foo1 = new Foo();
Foo foo2 = new Foo();
Foo foo3 = new Foo();
Integer i = new Integer(1);
foo1.set(i);
++i;
foo2.set(i);
++i;
foo3.set(i);

Which are the values of foo1, foo2 and foo3 now? You would expect them to be 1, 2 and 3. But when Integer would be mutable, they would now all be 3 because Foo.valuewould all point to the same Integer object.

现在 foo1、foo2 和 foo3 的值是什么?你会期望它们是 1、2 和 3。但是当 Integer 是可变的时,它们现在都是 3,因为它们Foo.value都指向同一个 Integer 对象。

回答by Michael Berry

However, wrapper classes represent primitive types, and primitive types (except String) are mutable.

然而,包装类代表原始类型,原始类型(String 除外)是可变的。

No they're not (and String isn't a primitive type). But since primitive types aren't objects anyway, they can't really be called mutable / immutable in the first place.

不,它们不是(并且 String 不是原始类型)。但是由于原始类型无论如何都不是对象,因此它们首先不能真正被称为可变/不可变。

Regardless, the fact that wrapper classes are immutable is a design decision (a good one IMO.) They could have just has easily been made mutable, or mutable alternatives provided too (indeed several libraries provide this, and other languages do by default.)

无论如何,包装类是不可变的这一事实是一个设计决定(一个很好的 IMO。)它们可以很容易地变得可变,或者也提供了可变的替代品(确实有几个库提供了这一点,而其他语言默认情况下这样做。)

回答by supercat

Any object instance which has anymutable aspects must have a unique identity; otherwise, another object instances which at one moment happened to be identical in every way except for its identity might at some other moment be different in its mutable aspect. In many cases, though, it's useful for types not to have an identity--to be able to pass a "4" without having to worry about which"4" one is passing. While there are times when it may be helpful to have a mutable wrapper of a primitive or immutable type, there are many more times when it's useful to have a type where all instances that hold the same data at some moment in time may be regarded as interchangeable.

其中有任何对象实例的任何可变方面必须有一个唯一的身份; 否则,在某一时刻碰巧除了其身份之外在各方面都相同的另一个对象实例在其他时刻可能在其可变方面有所不同。但是,在许多情况下,对于没有标识的类型来说,能够传递“4”而不必担心传递的是哪个“4”是很有用的。虽然有时拥有原始类型或不可变类型的可变包装器可能会有所帮助,但更多时候拥有一种类型是有用的,即在某个时刻持有相同数据的所有实例可能被视为可互换。

回答by Henry

The wrapper classes are immutable because it just makes no sense to be mutable.

包装类是不可变的,因为可变是没有意义的。

Consider following code:

考虑以下代码:

int n = 5;
n = 6;
Integer N = new Integer(n);

At first, it looks straightforward if you can change the value of N, just like you can change the value of n.

起初,如果您可以更改 N 的值,这看起来很简单,就像您可以更改 n 的值一样。

But actually N is not a wrapper to n, but a wrapper to 6! Look at the following line again:

但实际上 N 不是 n 的包装器,而是 6 的包装器!再次查看以下行:

Integer N = new Integer(n);

You are actually passing the value of n, which is 6, to N. And since Java is pass-by-value, you cannot pass n into N, to make N a wrapper to n.

您实际上是将 n 的值(即 6)传递给 N。由于 Java 是按值传递的,因此您无法将 n 传递给 N,从而使 N 成为 n 的包装器。

So, if we did add a set method to the wrapper:

因此,如果我们确实向包装器添加了一个 set 方法:

Integer N = new Integer(n);
N.setValue(7);
print(N); // ok, now it is 7
print(n); // oops, still 6!

The value of n will not be changed and that will be confusing!

n 的值不会改变,那会令人困惑!

Conclusion:

结论:

  1. wrapper classes are wrappers of values, not wrappers of the variables.

  2. it will be confusing if you did add a set method.

  3. if you know it is a wrapper of a value, you will no longer ask for a set method. For example, you will not do "6.setValue(7)".

  4. it's impossible to make a wrapper to a variable in Java.

  1. 包装器类是值的包装器,而不是变量的包装器。

  2. 如果您确实添加了 set 方法,则会令人困惑。

  3. 如果你知道它是一个值的包装器,你将不再需要一个 set 方法。例如,您不会执行“6.setValue(7)”。

  4. 在 Java 中不可能对变量进行包装。

回答by Scooter

The primitive types are mutable, but they are not shareable - that is no two sections of code will ever be referring to the same int variable (they are always passed by value). So you can change your copy and no one else sees the change, and vice versa. As Phillip shows in his answer, that would not be the case with mutable wrapper classes. So my guess is that they had a choice when the wrapped the primitive data types between:

原始类型是可变的,但它们不可共享——也就是说,没有两段代码会引用同一个 int 变量(它们总是按值传递)。因此,您可以更改您的副本,而其他人不会看到更改,反之亦然。正如菲利普在他的回答中所表明的那样,可变包装类的情况并非如此。所以我的猜测是,当包装原始数据类型时,他们有一个选择:

matching the fact that you can change the value of a primitive type,

匹配您可以更改原始类型的值的事实,

versus

相对

matching the fact that primitive types can be passed around and no changes by a user will be seen by any other user of the data.

匹配基本类型可以传递并且用户的任何更改都不会被数据的任何其他用户看到的事实。

And they chose the latter, which required immutability.

他们选择了后者,后者需要不变性。

回答by Vipin Jain

For example, consider the following java program:

例如,考虑以下 java 程序:

class WhyMutable 
{
    public static void main(String[] args) 
    {
        String name = "Vipin";
        Double sal = 60000.00;
        displayTax(name, sal);
    }

    static void displayTax(String name, Double num) {
        name = "Hello " + name.concat("!");
        num = num * 30 / 100;
        System.out.println(name + " You have to pay tax $" + num);
    }
}

Result: Hello Vipin! You have to pay tax 000.0

This is the case with pass by reference of wrapper class parameters as well. And, if strings and wrapper classes are non-final, anybody can extend those classes and write their own code to modify the wrapped primitive data. So, in order to maintain Data Integrity, the variables which we are using for data storage must be read-only,

包装类参数的引用传递也是这种情况。而且,如果字符串和包装类是非最终的,任何人都可以扩展这些类并编写自己的代码来修改包装的原始数据。因此,为了保持数据完整性,我们用于数据存储的变量必须是只读的,

i.e., Strings and Wrapper classes must be final & immutable and “pass by reference” feature should not be provided.

即,Strings 和 Wrapper 类必须是最终的且不可变的,并且不应提供“通过引用传递”功能。