Почему конечный объект может быть изменен?

Я столкнулся со следующим кодом в кодовой базе, я продолжаю работать:

public final class ConfigurationService {
    private static final ConfigurationService INSTANCE = new ConfigurationService();
    private List providers;

    private ConfigurationService() {
        providers = new ArrayList();
    }

    public static void addProvider(ConfigurationProvider provider) {
        INSTANCE.providers.add(provider);
    }

    ...

INSTANCE объявляется как final. Почему объекты могут быть добавлены к INSTANCE? Не был должен это делать недействительным использование финала. (Это не делает).

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

82
задан Michu93 20 January 2019 в 00:39
поделиться

7 ответов

final просто делает объект reference неизменным. Объект, на который она указывает, не является неизменяемым, если сделать так. INSTANCE никогда не может ссылаться на другой объект, но объект, на который он ссылается, может изменить состояние.

153
ответ дан 24 November 2019 в 09:04
поделиться

В языке Java нет встроенной концепции неизменяемости. Невозможно пометить методы как мутаторы. Следовательно, в языке нет способа обеспечить неизменность объекта.

2
ответ дан 24 November 2019 в 09:04
поделиться

final просто означает, что ссылку нельзя изменить. Вы не можете переназначить INSTANCE другой ссылке, если она объявлена ​​как final. Внутреннее состояние объекта по-прежнему изменчиво.

final ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;

вызовет ошибку компиляции

11
ответ дан 24 November 2019 в 09:04
поделиться

Быть окончательным - не то же самое, что быть неизменным.

final! = Immutable

Ключевое слово final используется для того, чтобы убедиться, что ссылка не изменена (то есть ссылка, которую она имеет, не может быть заменена новой)

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

Например

class SomeHighLevelClass {
    public final MutableObject someFinalObject = new MutableObject();
}

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

Таким образом, это невозможно:

....
SomeHighLevelClass someObject = new SomeHighLevelClass();
MutableObject impostor  = new MutableObject();
someObject.someFinal = impostor; // not allowed because someFinal is .. well final

Но если сам объект является изменяемым следующим образом:

class MutableObject {
     private int n = 0;

     public void incrementNumber() {
         n++;
     }
     public String toString(){
         return ""+n;
     }
}  

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

SomeHighLevelClass someObject = new SomeHighLevelClass();

someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();

System.out.println( someObject.someFinal ); // prints 3

Это имеет тот же эффект, что и ваше сообщение:

public static void addProvider(ConfigurationProvider provider) {
    INSTANCE.providers.add(provider);
}

Здесь вы не изменяете значение INSTANCE, вы изменяете его внутреннее состояние (с помощью метода provider.add)

, если вы хотите предотвратить это, класс определение следует изменить следующим образом:

public final class ConfigurationService {
    private static final ConfigurationService INSTANCE = new ConfigurationService();
    private List providers;

    private ConfigurationService() {
        providers = new ArrayList();
    }
    // Avoid modifications      
    //public static void addProvider(ConfigurationProvider provider) {
    //    INSTANCE.providers.add(provider);
    //}
    // No mutators allowed anymore :) 
....

Но это может не иметь особого смысла :)

Кстати, вы также должны синхронизировать доступ к нему в основном по той же причине.

33
ответ дан 24 November 2019 в 09:04
поделиться

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

Всегда помните, что когда вы объявляете переменную ссылочного типа, значение этой переменной является ссылкой, а не объектом.

24
ответ дан 24 November 2019 в 09:04
поделиться

Окончательный и неизменный - это не одно и то же. Final означает, что ссылку нельзя переназначить, поэтому нельзя сказать, что

INSTANCE = ...

Immutable означает, что сам объект не может быть изменен. Примером этого является класс java.lang.String . Вы не можете изменить значение строки.

4
ответ дан 24 November 2019 в 09:04
поделиться

После назначения переменной final она всегда содержит одно и то же значение. Если переменная final содержит ссылку на объект, то состояние объекта может быть изменено операциями над объектом, но переменная всегда будет ссылаться на один и тот же объект. Это применимо также к массивам, потому что массивы являются объектами; если переменная final содержит ссылку на массив, то компоненты массива могут быть изменены операциями с массивом, но переменная всегда будет ссылаться на один и тот же массив.

Источник

Вот руководство по созданию неизменяемого объекта .

6
ответ дан 24 November 2019 в 09:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: