CodeGym /Cursos /JAVA 25 SELF /Map.copyOf, Set.copyOf e outras utilidades

Map.copyOf, Set.copyOf e outras utilidades

JAVA 25 SELF
Nível 34 , Lição 1
Disponível

1. Métodos copyOf: List.copyOf, Set.copyOf, Map.copyOf

Imagine a situação: existe um método que deve retornar uma lista, um conjunto ou um mapa, mas é importante que o código chamador não consiga alterar o conteúdo dessa coleção. Por exemplo, “lista de moedas suportadas” ou “conjunto de papéis do usuário”. Qualquer inserção/remoção acidental pode quebrar a lógica de negócio.

Antes do Java 10, era preciso ou retornar manualmente uma cópia da coleção, ou usar wrappers como Collections.unmodifiableList(list). O wrapper protege apenas contra alterações feitas através dele, mas não contra modificações na coleção original.

Agora temos uma forma mais confiável — os métodos copyOf.

O que é isso?

Os métodos List.copyOf(Collection), Set.copyOf(Collection), Map.copyOf(Map) (Java 10) criam uma cópia verdadeiramente imutável da coleção ou mapa fornecidos.

  • A cópia não está ligada à coleção original: alterações no original não afetam o resultado de copyOf.
  • Alterar a cópia é impossível: qualquer modificação lançará UnsupportedOperationException.

Exemplo de criação de uma cópia imutável

import java.util.*;

public class CopyOfDemo {
    public static void main(String[] args) {
        List<String> modifiable = new ArrayList<>();
        modifiable.add("Java");
        modifiable.add("Python");

        // Criamos uma cópia imutável
        List<String> immutable = List.copyOf(modifiable);

        // Vamos tentar alterar a cópia
        try {
            immutable.add("C++"); // Lançará UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.out.println("Não é possível adicionar um elemento a uma coleção imutável!");
        }

        // Alteramos a lista original
        modifiable.add("Kotlin");

        // A cópia permaneceu inalterada!
        System.out.println("Lista original: " + modifiable);
        System.out.println("Cópia imutável: " + immutable);
    }
}

Saída do programa:

Não é possível adicionar um elemento a uma coleção imutável!
Lista original: [Java, Python, Kotlin]
Cópia imutável: [Java, Python]

Resumo de cada método

  • List.copyOf(Collection) — retorna uma lista imutável com os elementos da coleção original.
  • Set.copyOf(Collection) — retorna um conjunto imutável com os elementos da coleção; duplicatas serão removidas.
  • Map.copyOf(Map) — retorna um mapa imutável com os pares da origem.

Exemplo com 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);

        // Vamos tentar adicionar um elemento
        try {
            immutableSet.add("D");
        } catch (UnsupportedOperationException e) {
            System.out.println("Não é possível modificar o 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("Não é possível modificar o Map!");
        }
    }
}

2. Características e limitações dos métodos copyOf

Elementos null não são permitidos

Assim como os métodos de fábrica List.of, Set.of, Map.of, os métodos copyOf proíbem null como elemento, chave ou valor. Se houver null na coleção/mapa original, ao copiar será lançada NullPointerException.

List<String> listWithNull = Arrays.asList("A", null, "B");
List<String> immutable = List.copyOf(listWithNull); // Lançará NullPointerException!

A coleção é realmente imutável

Qualquer tentativa de add/remove/put/replace resulta em UnsupportedOperationException.

Original e cópia não estão ligados

Alterações na coleção original não afetam a cópia, e vice-versa.

Se a coleção já é imutável — o mesmo objeto é retornado

Se você passar uma coleção já imutável (por exemplo, o resultado de List.of(...)) — copyOf retornará o mesmo objeto (economia de memória).

List<String> immutable = List.of("X", "Y");
List<String> copy = List.copyOf(immutable);

System.out.println(immutable == copy); // true

A implementação concreta não é garantida

O tipo da coleção retornada é apenas List, Set ou Map. Não é necessariamente ArrayList, HashSet ou HashMap. Não conte com detalhes de implementação (por exemplo, tipo interno e otimizações); o contrato é a imutabilidade e o respectivo interface.

3. Diferença entre copyOf e wrappers (unmodifiable wrappers)

Collections.unmodifiableList(list) cria um wrapper sobre uma coleção existente. Se a coleção original for modificada, essas alterações também serão visíveis no wrapper.

List.copyOf(list) cria uma nova coleção imutável, não ligada ao original.

Demonstração da diferença

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);

        // Cópia
        List<String> copy = List.copyOf(original);

        // Vamos alterar o original
        original.add("C");

        System.out.println("Wrapper: " + wrapper); // [A, B, C]
        System.out.println("Cópia: " + copy);      // [A, B]
    }
}

Saída:

Wrapper: [A, B, C]
Cópia: [A, B]

O wrapper reflete as alterações do original, enquanto a cópia permanece inalterada.

4. CENÁRIOS práticos de uso de copyOf

Proteção de dados ao retornar de um método

public class CurrencyService {
    private final List<String> currencies = new ArrayList<>(List.of("USD", "EUR", "JPY"));

    public List<String> getSupportedCurrencies() {
        // Ninguém poderá modificar o resultado
        return List.copyOf(currencies);
    }
}

Agora o código chamador não poderá adicionar nem remover uma moeda da lista retornada.

Transferência entre camadas da aplicação

Quando os dados são transferidos entre camadas (DAO → serviço → controlador), use copyOf para ter certeza de que ninguém alterará a coleção “no caminho”.

Publicação segura em programas multithread

Coleções imutáveis são uma forma prática de compartilhar dados entre threads sem sincronização adicional.

5. Erros típicos ao usar copyOf e coleções imutáveis

Erro nº 1: tentativa de inserir null. Os métodos copyOf (assim como of) não aceitam null — nem como elemento de lista/conjunto, nem como chave/valor de mapa. Ao copiar, você receberá NullPointerException.

Erro nº 2: confusão entre wrapper e cópia. copyOf cria uma cópia independente: alterações no original não são visíveis na cópia. Já Collections.unmodifiableList(list) apenas envolve a coleção original: todas as mudanças no original serão refletidas no wrapper.

Erro nº 3: tentativa de modificar uma coleção imutável. Chamadas a add/remove/put em coleções de copyOf/of resultam em UnsupportedOperationException.

Erro nº 4: esquecer as limitações dos métodos de fábrica de mapas. Por exemplo, Map.of() suporta até 10 pares chave–valor. Se precisar de mais — use Map.ofEntries(...) ou monte um mapa mutável e depois aplique Map.copyOf.

Erro nº 5: confiar em uma implementação específica. Não espere que copyOf retorne exatamente ArrayList ou HashSet. Apenas o interface (List/Set/Map) e a imutabilidade são garantidos.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION