Я столкнулся со следующим кодом в кодовой базе, я продолжаю работать:
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
? Не был должен это делать недействительным использование финала. (Это не делает).
Я предполагаю, что ответ должен сделать что-то с указателями и памятью, но хотел бы знать наверняка.
final
просто делает объект reference неизменным. Объект, на который она указывает, не является неизменяемым, если сделать так. INSTANCE
никогда не может ссылаться на другой объект, но объект, на который он ссылается, может изменить состояние.
В языке Java нет встроенной концепции неизменяемости. Невозможно пометить методы как мутаторы. Следовательно, в языке нет способа обеспечить неизменность объекта.
final просто означает, что ссылку нельзя изменить. Вы не можете переназначить INSTANCE другой ссылке, если она объявлена как final. Внутреннее состояние объекта по-прежнему изменчиво.
final ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;
вызовет ошибку компиляции
Быть окончательным - не то же самое, что быть неизменным.
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 :)
....
Но это может не иметь особого смысла :)
Кстати, вы также должны синхронизировать доступ к нему в основном по той же причине.
Ключ к недоразумению - в названии вашего вопроса. Не объект является конечным, а переменная. Значение переменной не может измениться, но данные в ней могут.
Всегда помните, что когда вы объявляете переменную ссылочного типа, значение этой переменной является ссылкой, а не объектом.
Окончательный и неизменный - это не одно и то же. Final означает, что ссылку нельзя переназначить, поэтому нельзя сказать, что
INSTANCE = ...
Immutable означает, что сам объект не может быть изменен. Примером этого является класс java.lang.String
. Вы не можете изменить значение строки.
После назначения переменной
final
она всегда содержит одно и то же значение. Если переменнаяfinal
содержит ссылку на объект, то состояние объекта может быть изменено операциями над объектом, но переменная всегда будет ссылаться на один и тот же объект. Это применимо также к массивам, потому что массивы являются объектами; если переменнаяfinal
содержит ссылку на массив, то компоненты массива могут быть изменены операциями с массивом, но переменная всегда будет ссылаться на один и тот же массив.
Вот руководство по созданию неизменяемого объекта .