1. Méthodes copyOf : List.copyOf, Set.copyOf, Map.copyOf
Imaginez la situation suivante : une méthode doit renvoyer une liste, un ensemble ou une map, mais il est important que le code appelant ne puisse en aucune façon modifier le contenu de cette collection. Par exemple, « liste des devises prises en charge » ou « ensemble des rôles de l’utilisateur ». Toute insertion/suppression accidentelle peut rompre la logique métier.
Avant Java 10, il fallait soit retourner une copie de la collection manuellement, soit utiliser des wrappers comme Collections.unmodifiableList(list). Le wrapper protège seulement des modifications via lui-même, mais pas des modifications de la collection d’origine.
Nous disposons désormais d’un moyen plus fiable — les méthodes copyOf.
Qu’est-ce que c’est ?
Les méthodes List.copyOf(Collection), Set.copyOf(Collection), Map.copyOf(Map) (Java 10) créent une véritable copie immuable de la collection ou de la map passée.
- La copie n’est pas liée à la collection d’origine : les modifications de l’original n’affectent pas le résultat de copyOf.
- Modifier la copie est impossible : toute tentative de modification entraînera une UnsupportedOperationException.
Exemple de création d’une copie immuable
import java.util.*;
public class CopyOfDemo {
public static void main(String[] args) {
List<String> modifiable = new ArrayList<>();
modifiable.add("Java");
modifiable.add("Python");
// Création d'une copie immuable
List<String> immutable = List.copyOf(modifiable);
// Essayons de modifier la copie
try {
immutable.add("C++"); // Déclenchera UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Impossible d'ajouter un élément à une collection immuable !");
}
// Modification de la liste d'origine
modifiable.add("Kotlin");
// La copie est restée inchangée !
System.out.println("Liste d'origine: " + modifiable);
System.out.println("Copie immuable: " + immutable);
}
}
Sortie du programme :
Impossible d'ajouter un élément à une collection immuable !
Liste d'origine: [Java, Python, Kotlin]
Copie immuable: [Java, Python]
Bref aperçu de chaque méthode
- List.copyOf(Collection) — renvoie une liste immuable avec les éléments de la collection d’origine.
- Set.copyOf(Collection) — renvoie un ensemble immuable avec les éléments de la collection ; les doublons seront supprimés.
- Map.copyOf(Map) — renvoie une map immuable avec les paires de la map d’origine.
Exemple avec Set et Map
import java.util.*;
public class CopyOfSetMapDemo {
public static void main(String[] args) {
Set<String> modifiableSet = new HashSet<>(Set.of("A", "B", "C"));
Set<String> immutableSet = Set.copyOf(modifiableSet);
// Essayons d'ajouter un élément
try {
immutableSet.add("D");
} catch (UnsupportedOperationException e) {
System.out.println("Impossible de modifier le Set !");
}
Map<String, Integer> modifiableMap = new HashMap<>();
modifiableMap.put("Alice", 30);
modifiableMap.put("Bob", 25);
Map<String, Integer> immutableMap = Map.copyOf(modifiableMap);
try {
immutableMap.put("Charlie", 28);
} catch (UnsupportedOperationException e) {
System.out.println("Impossible de modifier la Map !");
}
}
}
2. Particularités et limitations des méthodes copyOf
Les éléments null ne sont pas autorisés
Comme les méthodes factory List.of, Set.of, Map.of, les méthodes copyOf interdisent null comme élément, clé ou valeur. S’il y a un null dans la collection/map d’origine, une NullPointerException sera levée lors de la copie.
List<String> listWithNull = Arrays.asList("A", null, "B");
List<String> immutable = List.copyOf(listWithNull); // Déclenchera NullPointerException !
La collection est réellement immuable
Toute tentative de add/remove/put/replace entraîne une UnsupportedOperationException.
L’original et la copie ne sont pas liés
Les modifications de la collection d’origine n’affectent pas la copie, et inversement.
Si la collection est déjà immuable — le même objet est renvoyé
Si vous passez une collection déjà immuable (par exemple, le résultat de List.of(...)) — copyOf renverra le même objet (économie de mémoire).
List<String> immutable = List.of("X", "Y");
List<String> copy = List.copyOf(immutable);
System.out.println(immutable == copy); // true
Aucune implémentation concrète n’est garantie
Le type de la collection renvoyée est simplement List, Set ou Map. Ce n’est pas nécessairement ArrayList, HashSet ou HashMap. Ne vous fiez pas aux détails d’implémentation (par exemple, le type interne et les optimisations) ; le contrat est l’immuabilité et l’interface correspondante.
3. Différence entre copyOf et les wrappers (unmodifiable wrappers)
Collections.unmodifiableList(list) crée un wrapper par-dessus une collection existante. Si la collection d’origine est modifiée, ces modifications sont visibles dans le wrapper.
List.copyOf(list) crée une nouvelle collection immuable, indépendante de l’original.
Démonstration de la différence
import java.util.*;
public class WrapperVsCopyOf {
public static void main(String[] args) {
List<String> original = new ArrayList<>(List.of("A", "B"));
// Wrapper
List<String> wrapper = Collections.unmodifiableList(original);
// Copie
List<String> copy = List.copyOf(original);
// Modifions l'original
original.add("C");
System.out.println("Wrapper: " + wrapper); // [A, B, C]
System.out.println("Copie: " + copy); // [A, B]
}
}
Sortie :
Wrapper: [A, B, C]
Copie: [A, B]
Le wrapper reflète les modifications de l’original, alors que la copie reste inchangée.
4. Scénarios pratiques d’utilisation de copyOf
Protection des données lors du retour d’une méthode
public class CurrencyService {
private final List<String> currencies = new ArrayList<>(List.of("USD", "EUR", "JPY"));
public List<String> getSupportedCurrencies() {
// Personne ne pourra modifier le résultat
return List.copyOf(currencies);
}
}
Désormais, le code appelant ne pourra pas ajouter ni supprimer une devise de la liste renvoyée.
Transmission entre couches de l’application
Lorsque les données sont transmises entre les couches (DAO → service → contrôleur), utilisez copyOf pour être sûr que personne ne modifie la collection en chemin.
Publication sûre dans des programmes multithread
Les collections immuables sont un moyen pratique de partager des données entre threads sans synchronisation supplémentaire.
5. Erreurs courantes avec copyOf et les collections immuables
Erreur n° 1 : tentative d’insérer null. Les méthodes copyOf (comme of) n’acceptent pas null — que ce soit comme élément de liste/ensemble ou comme clé/valeur de map. À la copie, vous obtiendrez une NullPointerException.
Erreur n° 2 : confusion entre wrapper et copie. copyOf crée une copie indépendante : les modifications de l’original ne sont pas visibles dans la copie. En revanche, Collections.unmodifiableList(list) ne fait qu’envelopper la collection d’origine : toutes les modifications de l’original seront reflétées dans le wrapper.
Erreur n° 3 : tentative de modifier une collection immuable. Les appels à add/remove/put sur les collections issues de copyOf/of entraînent une UnsupportedOperationException.
Erreur n° 4 : oublier les limites des méthodes factory des maps. Par exemple, Map.of() prend en charge jusqu’à 10 paires clé–valeur. Si vous en avez besoin de plus — utilisez Map.ofEntries(...) ou construisez une map mutable puis appliquez Map.copyOf.
Erreur n° 5 : se fier à une implémentation concrète. N’attendez pas que copyOf renvoie un ArrayList ou un HashSet précis. Seuls l’interface (List/Set/Map) et le caractère immuable sont garantis.
GO TO FULL VERSION