Может ли неконечный класс быть полностью неизменным? - PullRequest
2 голосов
/ 02 ноября 2011

Может ли класс, в котором нет окончательного модификатора, быть полностью неизменным?

Например, является ли следующий класс неизменным?

class Animal
{
    private String animalName;

    public Animal(String name) {
        animalName = name;
    }

    public String getName() { return animalName; }
}

Ответы [ 6 ]

8 голосов
/ 02 ноября 2011

Да.

Класс (как показано в вашем вопросе) является неизменным, поскольку ни одно из его внутреннего состояния не может быть изменено. Даже если вы определите подкласс класса Animal, он не сможет изменить animalName; однако, хотя класс Animal является неизменным, его подклассы могут быть или не быть неизменяемыми (в зависимости от их реализации).

Опасность этого заключается в том, что если кто-то определит подкласс как внутренний класс в классе Animal (как указано ниже), то он может нарушить вашу неизменность:

class Animal {
    private String animalName;
    public Animal(String name) {
        animalName = name;
    }
    public getName() { return animalName; }

    public class Eagle extends Animal {
        public Eagle() {
            super("Eagle");
        }
        public void foo() {
            animalName = animalName + "!";
        }
    }
}

По этой причине здорово использовать видимость private и модификатор final, где это возможно. Это предотвратит случайное введение людьми кода, который нарушает ограничения неизменяемости или инкапсуляции, которые вы намеревались навязать. Таким образом, со стороны программиста должно быть сознательное решение увеличить видимость или удалить ключевое слово final, и поэтому они не должны вводить никаких «аварий», как описано выше.

2 голосов
/ 02 ноября 2011

Нет, ваш класс Animal не является неизменным, потому что он позволяет создавать подклассы.

Почему бы и нет?

Смотрите этот пример подкласса:

public class ExceptionalAnimal extends Animal {

    public ExceptionalAnimal() {
        super(null);
    }


    @Override
    public String getName() {
        throw new AssertionError("Oops.. where did that come from?");
    }
}

Почему это важно?

Неизменяемость обычно используется для гарантии:

  1. что состояние объекта не меняется после постройки
  2. объекты являются поточно-ориентированными
  3. что объекты ведут себя определенным образом

Если класс допускает создание подклассов, нельзя полагаться ни на одну из этих гарантий. Если у вас есть метод, принимающий Animal в качестве параметра, любой может передать подкласс, который нарушает эти гарантии.

Исправлено: нет открытых или защищенных конструкторов

Один из часто используемых методов - не иметь конструкторов public или protected. Это предотвращает создание подклассов вне вашего пакета, и внутри вашего пакета вы все равно можете иметь свои собственные внутренние подклассы. Что вы не можете, если класс final.

Классы неизменяемой коллекции из библиотеки Google Guava используют эту технику. Из Javadoc :

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

Клиент может создавать ImmutableList с использованием статических методов of() и copyOf.

См. Также Effective Java , пункт 4: принудительное использование неотъемлемой части с помощью частного конструктора.

Имейте в виду, что гарантировать неизменность при отсутствии конструкторов public или protected проще, чем создать класс final. Например, Mockito позволит вам по умолчанию имитировать эти случаи:

ImmutableList notSoImmutable = mock(ImmutableList.class)
when(notSoImmutable.size()).thenThrow(new AssertionError("Oops.. where did that come from?"));

На пакетных индивидуальных занятиях

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

В качестве ответа я предположил, что Animal равно public. Если вы были заинтересованы именно в частных закрытых классах, игнорируйте мой ответ; -)

2 голосов
/ 02 ноября 2011

Написание неизменяемых классов легко. Класс будет неизменным, если выполняются все следующие условия:

1 All of its fields are final
2 The class is declared final
3 The this reference is not allowed to escape during construction
4 Any fields that contain references to mutable objects, such as arrays, collections, or mutable classes like Date:
    4.1 Are private
    4.2 Are never returned or otherwise exposed to callers
    4.3 Are the only reference to the objects that they reference
    4.4 Do not change the state of the referenced objects after construction
2 голосов
/ 02 ноября 2011

Да. Это полностью неизменным. Или, если быть более точным, его экземпляры полностью неизменны.

Могут (конечно) быть подклассы, которые не являются неизменяемыми ... но это не влияет на изменчивость экземпляра класса Animal.

0 голосов
/ 02 ноября 2011

Пока у вас нет открытого метода установки, как в вашем примере, объекты вашего класса будут неизменными.

В Java это можно обойти, используя Reflection API, так как он позволяет изменять всевиды полей.

Размещение модификатора final в классе означает, что он не может быть разделен на подклассы

0 голосов
/ 02 ноября 2011

Неизменяемые объекты - это просто объекты, состояние которых (данные объекта) не могут измениться после построения.

да, это полностью неизменный ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...