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

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

  • не может меняться как побочный эффект
  • легко рассуждать о своем состоянии
  • по сути безопасный поток
  • Не нужно предоставлять конструктор клонов/копий/factory
  • кэширование экземпляров
  • нет необходимости в защищенных копиях.

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

Ответ 1

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

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

Во-вторых, нет смысла говорить о том, что примитивные типы являются изменяемыми. Если вы измените значение переменной следующим образом:

int x = 5;
x = 6;

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

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

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

Ответ 2

Для некоторых типов существуют также изменяемые, поточно-безопасные обертки.

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

Плюс некоторые экзотические обертки

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

Ответ 3

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

Ответ 4

Вот пример, где было бы неплохо, если 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 будет указывать на один и тот же объект Integer.

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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

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

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, вы не можете передавать n в N, чтобы сделать N оболочкой n.

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

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

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

Вывод:

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

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

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

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

Ответ 8

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

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

против

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

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

Ответ 9

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

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

i.e, классы Strings и Wrapper должны быть окончательными и неизменными и "проходить по ссылке" не должны предоставляться.