Правила неизменяемого класса Java - PullRequest
4 голосов
/ 29 июня 2010

Является ли нижеследующий класс неизменным:

final class MyClass {
    private final int[] array;
    public MyClass(int[] array){
        this.array = array;
    }
}

Ответы [ 5 ]

21 голосов
/ 29 июня 2010

Нет, это не потому, что элементы массива все еще могут быть изменены.

int[] v1 = new int[10];
MyClass v2 = new MyClass(v1);
v1[0] = 42;  // mutation visible to MyClass1
6 голосов
/ 29 июня 2010

Мои два цента относительно правил неизменяемости (которые я сохранил после прочтения Effective Java - отличная книга!):

  1. Не предоставляйте методы, которые могут изменять состояние объекта.
  2. Сделайте все ваши поля окончательными.
  3. Убедитесь, что ваш класс не является расширяемым.
  4. Сделайте все ваши поля приватными.
  5. Предоставьте эксклюзивный доступ к любым полям иликомпоненты вашего класса, которые могут быть изменены.По сути это относится к вашей ситуации (как объяснил JaredPar ).Человек, который использует ваш класс, все еще имеет ссылку на ваш массив.В противном случае вы возвращаете ссылку на компонент вашего класса.В этом случае всегда создавайте защитных копий .В вашем случае вы не должны назначать ссылку.Вместо этого скопируйте массив, предоставленный пользователем вашего класса, во внутренний компонент.
1 голос
/ 29 июня 2010

«Неизменность» - это соглашение между программистом и им самим.Это соглашение может быть более или менее обеспечено компилятором.

Экземпляры класса являются «неизменяемыми», если они не изменяются во время нормального хода выполнения кода приложения.В некоторых случаях мы знаем , что они не меняются, потому что код фактически запрещает это;в других случаях это только часть того, как мы используем класс.Например, экземпляр java.util.Date формально изменяем (в нем есть метод setTime()), но обычно он обрабатывается так, как если бы он был неизменным;это всего лишь соглашение для всего приложения, что метод Date.setTime() не должен вызываться.

В качестве дополнительных примечаний:

  • Неизменяемость часто рассматривается в терминах "внешних характеристик",Например, Java String задокументирован как неизменный (это то, что говорит Javadoc).Но если вы посмотрите на исходный код, то увидите, что экземпляр String содержит закрытое поле с именем hash, которое со временем может измениться: это кэш для значения, возвращаемого hashCode().Мы по-прежнему говорим, что String является неизменным, потому что поле hash является внутренней оптимизацией, которая не имеет видимого извне эффекта.
  • С помощью отражения можно изменить самые частные поля экземпляра (включая отмеченныекак final), если программист так сильно хочет.Не то, чтобы это была хорошая идея: это может нарушить предположения, используемые другими частями кода, использующими указанный экземпляр.Как я уже сказал, неизменность - это соглашение: если программист хочет бороться с собой, он может, но это может иметь неблагоприятные побочные эффекты для производительности ...
  • Большинство значений Java на самом деле ссылки .Вы сами должны определить, является ли указанный объект частью того, что вы считаете «содержимым экземпляра».В вашем классе у вас есть поле, которое ссылается на (предоставленный извне) массив целых чисел.Если впоследствии содержимое этого массива будет изменено, сочтете ли вы, что это нарушает неизменность вашего экземпляра MyClass?На этот вопрос нет общего ответа.
0 голосов
/ 04 ноября 2015

Чтобы сделать класс неизменяемым, вам нужно убедиться, что все поля в нем являются окончательными, и что типы этих полей тоже неизменны.

Это может быть неприятно помнить, но есть инструмент, который поможет вам.

Pure4J предоставляет аннотацию @ImmutableValue, которую можно добавить в интерфейс или класс.

Существует плагин maven, который во время компиляции проверяет, соблюдаете ли вы следующие правила неизменности.

Надеюсь, это поможет.

0 голосов
/ 29 июня 2010

Нет способа сделать массив неизменным.То есть, нет никакого способа помешать любому клиентскому коду устанавливать, удалять или добавлять элементы в массив.

Вот действительно неизменная альтернатива:

private static class MyClass
{
    private List<Integer> list;

    private MyClass(final int[] array)
    {
        final List<Integer> tmplist = new ArrayList<Integer>(array.length);
        for (int i : array)
        {
            tmplist.add(array[i]);
        }
        this.list = Collections.unmodifiableList(tmplist);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...