Je peux voir que Collections.unmodifiableSet
renvoie une vue non modifiable de l'ensemble donné, mais je ne comprends pas pourquoi nous ne pouvons pas simplement utiliser le modificateur final
pour accomplir cela.
Dans ma compréhension, final
déclare une constante: quelque chose qui ne peut pas être modifié. Ainsi, si un ensemble est déclaré en tant que constante, il ne peut pas être modifié: rien ne peut être supprimé de l'ensemble et rien ne peut être ajouté.
Pourquoi avons-nous besoin de Collections.unmodifiableSet
?
final
déclare une référence d'objet qui ne peut pas être modifiée, par exemple.
private final Foo something = new Foo();
crée une nouvelle Foo
et place la référence dans something
. Par la suite, il n'est pas possible de modifier something
pour qu'il pointe vers une instance différente de Foo
.
Cela empêche pas de modifier l’état interne de l’objet. Je peux toujours appeler toutes les méthodes de Foo
accessibles au périmètre concerné. Si une ou plusieurs de ces méthodes modifient l'état interne de cet objet, alors final
ne l'empêchera pas.
À ce titre, les éléments suivants:
private final Set<String> fixed = new HashSet<String>();
est-ce que not crée une Set
qui ne peut pas être ajouté ou autrement modifié; cela signifie simplement que fixed
ne fera jamais référence à cette instance.
En revanche, en faisant:
private Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
crée une instance de Set
qui lancera UnsupportedOperationException
si on tente d'appeler fixed.add()
ou fixed.remove()
, par exemple - l'objet lui-même protégera son état interne et l'empêchera d'être modifié.
Par souci d'exhaustivité:
private final Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
crée une instance de Set
qui n'autorisera pas la modification de son état interne et signifie également que fixed
ne pointe jamais que vers une instance de cet ensemble.
La raison pour laquelle final
peut être utilisé pour créer des constantes de primitives est basée sur le fait que la valeur ne peut pas être modifiée. Rappelez-vous que fixed
ci-dessus n'était qu'une référence - une variable contenant une adresse qui ne peut pas être modifiée. Eh bien, pour les primitives, par exemple.
private final int ANSWER = 42;
la valeur de ANSWER
est celle de 42. Étant donné que ANSWER
ne peut pas être modifié, sa valeur sera toujours 42.
Voici un exemple qui brouille toutes les lignes:
private final String QUESTION = "The ultimate question";
Selon les règles ci-dessus, QUESTION
contient l'adresse d'une instance de String
qui représente "la question ultime" et cette adresse ne peut pas être modifiée. La chose à retenir ici est que String
est lui-même immuable - vous ne pouvez rien faire à une instance de String
qui le modifie, et toute opération qui le ferait autrement (telle que replace
, substring
, etc.) renvoie des références à instances de String
.
final
garantit uniquement que la référence de l'objet représenté par la variable ne peut pas être modifiée; il ne fait rien pour l'instance de l'objet et sa mutabilité.
final Set s = new Set();
garantit simplement que vous ne pourrez plus faire s = new Set();
. Cela ne rend pas le jeu non modifiable, mais si vous ne pouviez rien y ajouter pour commencer. Donc, pour que ce soit vraiment clair, final
n'affecte que la variable reference pas l'objet vers lequel la référence pointe.
Je peux faire ce qui suit:
final List<String> l = new ArrayList<String>();
l.add("hello");
l.add("world");
l.remove(0);
mais je ne peux pas faire ça.
l = new ArrayList<String>();
encore une fois à cause de la variable final
, je ne peux pas modifier le sens de la variable l.
vous devez effectuer l'une des trois opérations suivantes pour sécuriser un thread de conteneur Collection.
Java.util.Collections.syncronizedXXX();
ou
Java.util.Collections.unmodifiableXXX();
ou utilisez l'un des conteneurs appropriés à partir de Java.util.concurrency.* package
.
si j'avais un objet Person
et que j'avais final Person p = new Person("me");
, cela signifie que je ne peux pas réaffecter p
pour qu'il pointe vers un autre objet Person
. Je peux encore faire p.setFirstName("you");
Ce qui confond la situation est que
final int PI = 3.14;
final String greeting = "Hello World!";
ressemblent à const
en C++, alors que les objets qu’ils désignent sont immuables/non modifiables par nature. Les conteneurs ou les objets utilisant des méthodes de mutateur pouvant modifier l'état interne de l'objet ne sont pas const
, mais uniquement les référence de ces objets sont final
et ne peuvent pas être réaffectés à reference un autre objet.
final
n'est pas (style C++) const
. Contrairement à C++, Java n'a pas de méthode const
- ou quoi que ce soit du genre, et les méthodes qui peuvent modifier l'objet peuvent être appelées via une référence final
.
Collections.unmodifiable*
est un wrapper qui applique (au moment de l'exécution, pas au moment de la compilation) la lecture seule pour la collection concernée.
La Collections.unmodifiableSet(Set<? extends T>)
créera un wrapper sur le jeu d'origine. Cet ensemble de wrapper ne peut pas être modifié. mais le jeu d'origine peut toujours être modifié.
Exemple:
Set<String> actualSet=new HashSet<String>(); //Creating set
Ajout de quelques éléments
actualSet.add("aaa");
actualSet.add("bbb");
Impression d'éléments ajoutés
System.out.println(actualSet); //[aaa, bbb]
Placez la actualSet
dans un ensemble non modifiable et attribuez-la à une nouvelle référence (wrapperSet
).
Set<String> wrapperSet=Collections.unmodifiableSet(orginalSet);
Imprimez le wrapperSet. donc il aura actualSet
valeurs
System.out.println(wrapperSet); //[aaa, bbb]
essayons de supprimer/ajouter un élément sur wrapperSet
.
wrapperSet.remove("aaa"); //UnSupportedOperationException
Ajouter un élément de plus dans actualSet
actualSet .add("ccc");
Imprimer actualSet
et wrapperSet
. les deux valeurs sont identiques. donc, si vous ajoutez/supprimez des éléments dans le jeu réel, les modifications seront également reflétées dans le jeu de wrapper.
System.out.println(actualSet); //[aaa, ccc, bbb]
System.out.println(wrapperSet); // [aaa, ccc, bbb]
Utilisation:
Cette Collections.unmodifiableSet(Set<? extends T>)
est utilisée pour empêcher la modification de la méthode getter de Set pour tout objet. disons
public class Department{
private Set<User> users=new HashSet<User>();
public Set<User> getUsers(){
return Collections.unmodifiableSet(users);
}
}
Résumez ce que nous pouvons faire et ne pouvons pas:
Préparation:
private Set<String> words = new HashSet<>(Arrays.asList("existing Word"));
private final Set<String> words = new HashSet<>();
peut :
words.add("new Word");
ne peut pas :
words = new HashSet<>(); //compilation error
private final Set words = Collections.unmodifiableSet (mots);
peut :
String Word = words.iterator().next();
ne peut pas :
words = new HashSet<>(); // compilation error
words.add("new Word"); // runtime error UnsupportedOperationException
Mais si vous avez la collection avec mutual objects, vous pouvez CHANGER l'état interne de cet objet.
class A {
public int a; //mutable field. I can change it after initialization
public A(int a) {this.a = a;}
}
private final Set<A> set = Collections.unmodifiableSet(Arrays.asList(new A(25)));
Je ne peux toujours pas
set = new HashSet<>(); // compilation error
set.add(new A(777)); // runtime error UnsupportedOperationException
Mais can
A custom = words.iterator().next(); //here custom.a = 25;
custom.a = 777; //here first element of **final, unmodifible** collection
//was changed from 25 to 777