2010-03-10 2 views
24

Я вижу, что Collections.unmodifiableSet возвращает немодифицируемый вид данного набора, но я не понимаю, почему мы не можем просто использовать модификатор final для достижения этого.Что делает Collections.unmodifiableSet() в Java?

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

Зачем нам Collections.unmodifiableSet?

ответ

51

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

private final Foo something = new Foo(); 

создает новый Foo и помещает ссылку в something.После этого невозможно изменить something, чтобы указать на другой экземпляр Foo.

Это не предотвращает изменение внутреннего состояния объекта. Я все еще могу вызывать любые методы на Foo, доступные для соответствующей области. Если один или несколько из этих методов изменят внутреннее состояние этого объекта, то final не предотвратит это.

Таким образом, следующее:

private final Set<String> fixed = new HashSet<String>(); 

делает не создать Set, которые не могут быть добавлены или иным образом изменены; это просто означает, что 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"; 

В соответствии с правилами выше, QUESTION содержит адрес экземпляра String, который представляет «Окончательный вопрос», и этот адрес не может быть изменен. Здесь нужно помнить, что сам String неизменен - ​​вы не можете ничего сделать с экземпляром String, который его изменяет, и любые операции, которые в противном случае это сделали (например, replace, и т. Д.) Возвращают ссылки на совершенно разные экземпляры String.

14

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 я не могу изменить то, что переменные точки л в.

Вам необходимо выполнить одно из следующих трех действий, чтобы сделать поток контейнера коллекции безопасным.

java.util.Collections.syncronizedXXX(); 

или

java.util.Collections.unmodifiableXXX(); 

или использование одной из соответствующих контейнеров из java.util.concurrency.* package.

, если у меня был Person объект и сделал final Person p = new Person("me"); это означает, что я не могу переназначить p, чтобы указать на другой Person объекта. Я все еще могу сделать p.setFirstName("you");

Что запутывает ситуацию в том, что

final int PI = 3.14; 
final String greeting = "Hello World!"; 

выглядеть const в C++, когда на самом деле объекты, которые они указывают на неизменны/неизменяемым по своей природе. Контейнеры или объекты с мутаторными методами, которые могут изменить внутреннее состояние объекта, не являются const только ссылкой к этим объектам: final и не могут быть переназначены до . Ссылка другой объект.

+1

Хороший пост. Короче говоря, _reference_ не может быть изменено, но _contents_ объекта * может *. – extraneon

3

final не является (C++ - style) const. В отличие от C++, Java не имеет методов const или чего-либо подобного, и методы, которые могут изменить объект, могут быть вызваны с помощью ссылки final.

Collections.unmodifiable* - это оболочка, которая обеспечивает (только во время выполнения, а не во время компиляции) доступность только для чтения для соответствующей коллекции.

0

Collections.unmodifiableSet(Set<? extends T>) создаст обертку на исходном наборе. Этот набор оберток не может быть изменен. но все же исходный набор можно изменить.

Пример:

Set<String> actualSet=new HashSet<String>(); //Creating set 

Добавление некоторых элементов

actualSet.add("aaa"); 
actualSet.add("bbb"); 

Печать добавили элементы

System.out.println(actualSet); //[aaa, bbb] 

Поместите actualSet в неизменяемо набора и присвоенные новой ссылки (wrapperSet).

Set<String> wrapperSet=Collections.unmodifiableSet(orginalSet); 

Распечатать упаковкуНабор. поэтому он будет иметь значения actualSet

System.out.println(wrapperSet); //[aaa, bbb] 

позволяет попытаться удалить/добавить один элемент на wrapperSet.

wrapperSet.remove("aaa"); //UnSupportedOperationException 

Добавить еще один элемент в actualSet

actualSet .add("ccc"); 

Печать actualSet и wrapperSet. оба значения набора одинаковы. поэтому, если вы добавляете/удаляете какие-либо элементы при фактическом наборе, изменения также будут отражены в наборе оберток.

System.out.println(actualSet); //[aaa, ccc, bbb] 
    System.out.println(wrapperSet); // [aaa, ccc, bbb] 

Использование:

Этот Collections.unmodifiableSet(Set<? extends T>) используется для предотвращения модификации метода геттерного Сета любого объекта. let say

public class Department{ 

    private Set<User> users=new HashSet<User>(); 

    public Set<User> getUsers(){ 
     return Collections.unmodifiableSet(users); 
    } 
} 
Смежные вопросы