Я вижу это Collections.unmodifiableSet
возвращает немодифицируемое представление данного набора, но я не понимаю, почему мы не можем просто использовать final
модификатор для выполнения этого.
В моем понимании, final
объявляет константу: что-то, что не может быть изменено. Так, если набор объявляется как константа затем, он не может быть изменен: ничто не может быть удалено из набора, и ничто не может быть добавлено.
Почему нам нужно Collections.unmodifiableSet
?
final
объявляет ссылку на объект, которую нельзя изменить, например
private final Foo something = new Foo();
создает новый Foo
и помещает ссылку в something
. После этого невозможно изменить что-то
, чтобы указать на другой экземпляр Foo
.
Это не предотвращает изменение внутреннего состояния объекта. Я все еще могу вызывать любые методы на Foo
, доступные для соответствующей области. Если один или несколько из этих методов изменяют внутреннее состояние этого объекта, то final
не предотвратит этого.
Таким образом, следующее:
private final Set<String> fixed = new HashSet<String>();
не создает Набор
, который нельзя добавить или изменить иным образом; это просто означает, что fixed
будет ссылаться только на этот экземпляр.
Напротив, выполнение:
private Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
создает экземпляр Set
, который генерирует исключение UnsupportedOperationException
, если кто-то пытается вызвать fixed.add ()
или Например, fixed.remove ()
- сам объект будет защищать свое внутреннее состояние и предотвращать его изменение.
Для полноты:
private final Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
создает экземпляр Set
, который не позволяет изменять его внутреннее состояние, а также означает, что fixed
будет указывать только на экземпляр этого набора.
Причина, по которой final
может использоваться для создания констант примитивов, основана на том факте, что значение нельзя изменить. Помните, что fixed
выше была просто ссылкой - переменной, содержащей адрес, который нельзя изменить. Ну, для примитивов, например
private final int ANSWER = 42;
значение ANSWER
равно 42. Поскольку ANSWER
нельзя изменить, он всегда будет иметь только значение 42.
Пример, который размывает все линии будет следующим:
private final String QUESTION = "The ultimate question";
Согласно правилам, приведенным выше, ВОПРОС
содержит адрес экземпляра String
, который представляет «Главный вопрос», и этот адрес не может быть изменен. Здесь следует помнить, что String
сам по себе неизменяемый - вы не можете ничего сделать с экземпляром String
, который его изменяет, а также с любыми операциями, которые в противном случае сделали бы это (например, replace
, substring
и т. Д.) Возвращают ссылки на совершенно разные экземпляры String
.
final
не является (C++-style) const
. В отличие от C++, Java не имеет const
-методов или чего-то подобного, и методы, которые могут изменять объект, могут быть вызваны через ссылку final
.
Collections.unmodifiable*
— это оболочка, которая обеспечивает (только во время выполнения, а не во время компиляции) только чтение для соответствующей коллекции.
final
только гарантирует, что ссылка на объект, который представляет переменная, не может быть изменена, она ничего не делает для экземпляра объекта и его изменчивости.
final Set s = new Set ();
просто гарантирует, что вы не сможете снова выполнить s = new Set ();
. Это не делает набор неизменяемым, если бы вы не могли ничего добавить к нему для начала.Итак, чтобы было действительно ясно, final
влияет только на переменную ссылка , а не на объект, на который указывает ссылка.
Я могу сделать следующее:
final List<String> l = new ArrayList<String>();
l.add("hello");
l.add("world");
l.remove(0);
но я не могу этого сделать.
l = new ArrayList<String>();
снова из-за final
я не могу изменить то, на что указывает переменная l.
Вы должны выполнить одно из следующих трех действий, чтобы сделать контейнер Collection потокобезопасным.
java.util.Collections.syncronizedXXX();
,
java.util.Collections.unmodifiableXXX();
или
используйте один из подходящих контейнеров из java.util.concurrency. * Package
.
если у меня был объект Person
и я сделал final Person p = new Person («я»);
это означает, что я не могу переназначить p
на указать на другой объект Person
. Я все еще могу сделать p.setFirstName ("you");
Ситуацию смущает то, что
final int PI = 3.14;
final String greeting = "Hello World!";
в C ++ выглядит как const
, тогда как на самом деле объекты, на которые они указывают, являются неизменный / неизменяемый по своей природе. Контейнеры или объекты с методами мутатора, которые могут изменять внутреннее состояние объекта, не const
, просто ссылка на эти объекты являются окончательными
и не могут быть переназначены к ссылка на другой объект.