Почему классы оболочки Java неизменяемы?

Я знаю обычные причины, которые применяются к общим неизменяемым классам, а именно

  1. не может измениться как побочный эффект
  2. легко рассуждать об их состоянии
  3. по своей сути потокобезопасный
  4. нет необходимости предоставлять clone / copy constructor / factory copy method
  5. экземпляр кэширование
  6. нет необходимости в защитных копиях.

однако классы-оболочки представляют примитивные типы, а примитивные типы изменчивый. Так почему же классы-оболочки не изменчивы?

9 ответов


однако классы-оболочки представляют примитивные типы, а примитивные типы (кроме String) изменчивы.

во-первых, String не является примитивным типом.

во-вторых, нет смысла говорить о том, что примитивные типы изменчивы. Если вы измените значение переменная такой:

int x = 5;
x = 6;

это не изменение числа 5-это изменение значения x.

пока типы оболочки смогли были сделаны изменчивыми, это было бы раздражающе, на мой взгляд. Я часто использую коллекции только для чтения этих типов и не хотел бы, чтобы они были изменяемыми. Очень иногда мне нужен изменяемый эквивалент, но в этом случае достаточно легко придумать его или использовать Atomic* классы.

я обнаружил, что желаю этого Date и Calendar были неизменяемы гораздо чаще, чем мне хотелось Integer изменяемый... (Конечно, я обычно достигаю времени Джоды вместо этого, но одно из преимуществ Joda Time is неизменяемости.)


есть изменяемые, потокобезопасные обертки, а также для некоторых типов.

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

плюс некоторые экзотические фантики

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

для вашей информации: если вы хотите изменяемые классы держателя, вы можете использовать атомарные * классы в , например,AtomicInteger, AtomicLong


вот пример, где было бы очень плохо, когда 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);

какие значения foo1, foo2 и foo3 теперь? Вы ожидаете, что они будут 1, 2 и 3. Но когда Integer будет изменяемым, теперь все они будут 3, потому что Foo.value все будут указывать на один и тот же целочисленный объект.


однако классы-оболочки представляют примитивные типы, а примитивные типы (кроме String) изменчивы.

нет, они не являются (и String не является примитивом). Но поскольку примитивные типы все равно не являются объектами, их нельзя назвать изменяемыми / неизменяемыми в первую очередь.

независимо от того, тот факт, что классы оболочки неизменяемы, является проектным решением(хорошим IMO.) Thye мог бы просто легко быть сделан изменяемым или изменяемыми альтернативами при условии тоже (действительно, несколько библиотек предоставляют это, и другие языки делают по умолчанию.)


любой экземпляр объекта, который имеет любой изменяемые аспекты должны иметь уникальный личность; в противном случае другие экземпляры объекта, которые в какой-то момент были идентичны во всех отношениях, за исключением его идентичности, могли бы в какой-то другой момент отличаться в своем изменчивом аспекте. Во многих случаях, однако, полезно для типов не иметь идентичности-чтобы иметь возможность передать "4", не беспокоясь о , который "4" - это передача. Хотя бывают моменты, когда может быть полезно иметь изменяемую оболочку примитивного или неизменяемого типа, есть еще много раз, когда полезно иметь тип, где все экземпляры, которые содержат одни и те же данные в какой-то момент времени, могут рассматриваться как взаимозаменяемые.


классы оболочки неизменяемы, потому что просто нет смысла быть изменяемыми.

рассмотрим следующий код:

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

сначала это выглядит просто, если вы можете изменить значение N, как можно изменить значение N.

но на самом деле N-это не обертка для n, а обертка для 6! Посмотрите еще раз на следующую строку:

Integer N = new Integer(n);

вы фактически передаете значение n, которое равно 6, в N. И так как Java является pass-by-value, невозможно передать Н В Н, сделать обертку.

Итак, если мы добавили метод set в оболочку:

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

значение n не будет изменено, и это будет путать!

вывод:

  1. классы-оболочки являются оболочками значений, а не оболочками переменных.

  2. это будет запутанным, если вы добавили метод set.

  3. Если вы знаете, что это обертка value, вы больше не будете запрашивать метод set. Например, вы не будете делать "6.setValue (7)".

  4. невозможно сделать оболочку переменной в Java.


примитивные типы изменчивы, но они не являются общими - то есть никакие два раздела кода никогда не будут ссылаться на одну и ту же переменную int (они всегда передаются по значению). Таким образом, вы можете изменить свою копию, и никто другой не видит изменения, и наоборот. Как показывает Филипп в своем ответе, это не относится к изменяемым классам обертки. Поэтому я предполагаю, что у них был выбор, когда завернутые примитивные типы данных между:

соответствующие тому, что вы можете изменить значение примитивного типа,

и

соответствие тому факту, что примитивные типы могут быть переданы, и никакие изменения пользователем не будут замечены любым другим пользователем данных.

и они выбрали последнее, что требовало неизменности.


например, рассмотрим следующую программу 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

это относится и к pass by reference параметров класса оболочки. И если строки и классы-оболочки не являются окончательными, любой может расширить эти классы и написать свой собственный код для изменения завернутых примитивных данных. Таким образом, для сохранения целостности данных переменные, которые мы используем для хранения данных, должны быть доступны только для чтения,

т. е. строки и классы-оболочки должны быть окончательными & незыблемые и "перевал по ссылке " функция не должна предоставляться.