Подтвердить что ты не робот

Мутируемый класс как ребенок неизменяемого класса

Я хочу иметь неизменяемые объекты Java, подобные этому (сильно упрощенному):

class Immutable {

    protected String name;

    public Immutable(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

В некоторых случаях объект должен быть не только читаемым, но и изменяемым, поэтому я мог бы добавить изменчивость через наследование:

public class Mutable extends Immutable {

    public Mutable(String name) {
        super(name);
    }

    public void setName(String name) {
        super.name = name;
    }

}

Хотя это технически хорошо, мне интересно, соответствует ли он ООП и наследованию, что mutable также является типом неизменяемым. Я хочу избежать преступления ООП, чтобы бросить UnsupportedOperationException для неизменяемого объекта, как это делает API коллекций Java.

Как вы думаете? Любые другие идеи?

4b9b3361

Ответ 1

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

Joda Time использует "ReadableXXX", чтобы дать понять, что "этот класс предоставляет доступ только для чтения, другие подклассы могут быть изменчивыми". Я не уверен, нравится ли мне эта идея, но я не могу сказать, что видел много альтернатив.

В основном проблема заключается в выражении отрицательного - Immutable описывает то, что вы не можете сделать (мутировать), и это не может быть разумно применено в подклассах. (Даже если поля в Immutable были окончательными, это не остановило бы подкласс, имеющий свои измененные поля.)

Ответ 2

Я бы предположил, что у вас должен быть наследуемый базовый класс "ReadableFoo", производный закрытый класс ImmutableFoo и другие производные классы MutableFoo. Код, который не заботится о том, изменен ли Foo или нет, может принять ReadableFoo. Код, который хочет Foo, который гарантированно не изменится, может принять ImmutableFoo. Код, который может понадобиться для изменения Foo, может принимать MutableFoo.

Обратите внимание, что конструкторы как для ImmutableFoo, так и для MutableFoo обычно должны принимать ReadableFoo. Таким образом, любой Foo будет конвертирован в изменяемую или неизменяемую версию.

Ответ 3

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

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

Вот почему строка является окончательной.

Ответ 5

Я нахожу ваш код довольно любопытным.

Чтобы реализовать такое неизменяемое поведение, я предпочел бы использовать интерфейс Immutable, предоставляющий только метод getter, в то время как объект содержит оба. Таким образом, операции, основанные на неизменяемых объектах, вызвали бы интерфейс, в то время как другие назвали бы объект.

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