1. Metodi copyOf: List.copyOf, Set.copyOf, Map.copyOf
Immaginate una situazione: esiste un metodo che deve restituire una lista, un insieme o una mappa, ma è importante che il codice chiamante non possa in alcun modo modificare il contenuto di quella collezione. Ad esempio, «elenco delle valute supportate» o «insieme dei ruoli dell'utente». Qualsiasi inserimento/eliminazione accidentale può compromettere la logica di business.
Fino a Java 10 si doveva o restituire una copia della collezione manualmente, oppure usare wrapper come Collections.unmodifiableList(list). Il wrapper protegge solo dalle modifiche effettuate attraverso se stesso, ma non dalle modifiche della collezione originale.
Ora abbiamo un modo più affidabile — i metodi copyOf.
Che cosa sono?
I metodi List.copyOf(Collection), Set.copyOf(Collection), Map.copyOf(Map) (Java 10) creano una vera copia immutabile della collezione o mappa passata.
- La copia non è collegata alla collezione originale: le modifiche all'originale non influiscono sul risultato copyOf.
- Modificare la copia è impossibile: qualsiasi tentativo di modifica genera UnsupportedOperationException.
Esempio di creazione di una copia immutabile
import java.util.*;
public class CopyOfDemo {
public static void main(String[] args) {
List<String> modifiable = new ArrayList<>();
modifiable.add("Java");
modifiable.add("Python");
// Creiamo una copia immutabile
List<String> immutable = List.copyOf(modifiable);
// Proviamo a modificare la copia
try {
immutable.add("C++"); // Solleverà UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("Impossibile aggiungere un elemento a una collezione immutabile!");
}
// Modifichiamo la lista originale
modifiable.add("Kotlin");
// La copia è rimasta invariata!
System.out.println("Lista originale: " + modifiable);
System.out.println("Copia immutabile: " + immutable);
}
}
Output del programma:
Impossibile aggiungere un elemento a una collezione immutabile!
Lista originale: [Java, Python, Kotlin]
Copia immutabile: [Java, Python]
Sintesi di ciascun metodo
- List.copyOf(Collection) — restituisce una lista immutabile con gli elementi della collezione originale.
- Set.copyOf(Collection) — restituisce un insieme immutabile con gli elementi della collezione; i duplicati verranno eliminati.
- Map.copyOf(Map) — restituisce una mappa immutabile con le coppie della mappa originale.
Esempio con Set e 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);
// Proviamo ad aggiungere un elemento
try {
immutableSet.add("D");
} catch (UnsupportedOperationException e) {
System.out.println("Impossibile modificare 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("Impossibile modificare Map!");
}
}
}
2. Caratteristiche e limitazioni dei metodi copyOf
Gli elementi null non sono ammessi
Come i metodi factory List.of, Set.of, Map.of, i metodi copyOf vietano null come elemento, chiave o valore. Se nella collezione/mappa di origine è presente null, durante la copia verrà sollevata NullPointerException.
List<String> listWithNull = Arrays.asList("A", null, "B");
List<String> immutable = List.copyOf(listWithNull); // Solleverà NullPointerException!
La collezione è davvero immutabile
Qualsiasi tentativo di add/remove/put/replace comporta UnsupportedOperationException.
Originale e copia non sono collegati
Le modifiche alla collezione originale non influiscono sulla copia, e viceversa.
Se la collezione è già immutabile — viene restituita la stessa istanza
Se passate una collezione già immutabile (per esempio il risultato List.of(...)) — copyOf restituirà lo stesso oggetto (risparmio di memoria).
List<String> immutable = List.of("X", "Y");
List<String> copy = List.copyOf(immutable);
System.out.println(immutable == copy); // true
L'implementazione concreta non è garantita
Il tipo della collezione restituita è semplicemente List, Set o Map. Non è necessariamente ArrayList, HashSet o HashMap. Non fate affidamento sui dettagli di implementazione (per esempio, tipo interno e ottimizzazioni); il contratto è l'immutabilità e l'interfaccia corrispondente.
3. Differenza tra copyOf e i wrapper non modificabili (unmodifiable wrappers)
Collections.unmodifiableList(list) crea un wrapper sopra una collezione esistente. Se la collezione originale viene modificata, queste modifiche si vedranno anche nel wrapper.
List.copyOf(list) crea una nuova collezione immutabile, non collegata all'originale.
Dimostrazione della differenza
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);
// Copia
List<String> copy = List.copyOf(original);
// Modifichiamo l'originale
original.add("C");
System.out.println("Wrapper: " + wrapper); // [A, B, C]
System.out.println("Copia: " + copy); // [A, B]
}
}
Output:
Wrapper: [A, B, C]
Copia: [A, B]
Il wrapper riflette le modifiche dell'originale, mentre la copia rimane invariata.
4. Casi d'uso pratici di copyOf
Protezione dei dati al ritorno da un metodo
public class CurrencyService {
private final List<String> currencies = new ArrayList<>(List.of("USD", "EUR", "JPY"));
public List<String> getSupportedCurrencies() {
// Nessuno potrà modificare il risultato
return List.copyOf(currencies);
}
}
Ora il codice chiamante non potrà aggiungere o rimuovere una valuta dall'elenco restituito.
Passaggio tra i livelli dell'applicazione
Quando i dati vengono passati tra i livelli (DAO → servizio → controller), utilizzate copyOf per essere certi che nessuno modificherà la collezione «per strada».
Pubblicazione sicura nei programmi multithread
Le collezioni immutabili sono un modo comodo per «condividere» i dati tra thread in modo sicuro senza sincronizzazione aggiuntiva.
5. Errori tipici nell'uso di copyOf e delle collezioni immutabili
Errore n. 1: tentativo di inserire null. I metodi copyOf (così come of) non tollerano null — né come elemento di lista/insieme, né come chiave/valore di mappa. Durante la copia otterrete NullPointerException.
Errore n. 2: confusione tra wrapper e copia. copyOf crea una copia indipendente: le modifiche dell'originale non sono visibili nella copia. Invece Collections.unmodifiableList(list) si limita ad avvolgere la collezione originale: tutte le modifiche dell'originale si rifletteranno nel wrapper.
Errore n. 3: tentativo di modificare una collezione immutabile. Le chiamate add/remove/put sulle collezioni create con copyOf/of generano UnsupportedOperationException.
Errore n. 4: dimenticare i limiti dei metodi factory per le mappe. Per esempio, Map.of() supporta fino a 10 coppie chiave–valore. Se ne servono di più — utilizzate Map.ofEntries(...) oppure costruite una mappa modificabile e poi applicate Map.copyOf.
Errore n. 5: fare affidamento su un'implementazione concreta. Non aspettatevi che copyOf restituisca proprio ArrayList o HashSet. Sono garantiti solo l'interfaccia (List/Set/Map) e l'immutabilità.
GO TO FULL VERSION