0Pricing
Java Academy · Aula

Curingas em APIs

Projete métodos genéricos flexíveis.

Curingas em APIs é uma aula grátis de Java Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Java Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Java Academy inclui 4 aulas no total.

Projetando métodos flexíveis

Os curingas são principalmente uma ferramenta para projetistas de API. Um curinga bem posicionado permite que os chamadores passem mais tipos sem conversões explícitas.

O objetivo é oferecer flexibilidade máxima aos chamadores e segurança completa de tipos internamente.

import java.util.List;

public class Main {
    static double total(List<? extends Number> items) {
        double sum = 0;
        for (Number n : items) sum += n.doubleValue();
        return sum;
    }
    public static void main(String[] args) {
        System.out.println(total(List.of(1, 2, 3)));
        System.out.println(total(List.of(1.5, 2.5)));
    }
}

Curingas sem limite

List<?> é um curinga sem limite: uma lista de algum tipo desconhecido.

Use-o quando o método não se importar com o tipo dos elementos, por exemplo, ao contar elementos ou verificar se a lista está vazia.

import java.util.List;

public class Main {
    static int size(List<?> any) {
        return any.size();
    }
    public static void main(String[] args) {
        System.out.println(size(List.of("a", "b")));
        System.out.println(size(List.of(1, 2, 3)));
    }
}

List<?> versus List<Object>

Esses tipos são diferentes. List<Object> aceita apenas uma lista declarada como Object. List<?> aceita uma lista de qualquer tipo.

Você pode passar uma List<String> para um parâmetro List<?>, mas não para um parâmetro List<Object>.

import java.util.List;

public class Main {
    static void printAll(List<?> list) {
        for (Object o : list) System.out.println(o);
    }
    public static void main(String[] args) {
        List<String> names = List.of("Ada", "Linus");
        printAll(names); // works; List<Object> would reject this
    }
}

Quando um parâmetro de tipo é mais claro

Se você precisar fazer referência ao tipo do elemento mais de uma vez, um parâmetro de tipo nomeado fica mais claro do que um curinga.

Compare void swap(List<?> l, int i, int j) (que precisa de um auxiliar privado) com uma versão genérica mais clara.

import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> void swap(List<T> list, int i, int j) {
        T tmp = list.get(i);
        list.set(i, list.get(j));
        list.set(j, tmp);
    }
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(List.of("a", "b", "c"));
        swap(list, 0, 2);
        System.out.println(list);
    }
}

O truque da captura com um auxiliar

Se uma API pública precisar usar List<?>, você poderá delegar a operação a um auxiliar genérico privado para capturar o curinga.

O auxiliar dá nome ao tipo T para que possa escrever novamente na lista.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void reverse(List<?> list) { reverseHelper(list); }
    private static <T> void reverseHelper(List<T> list) {
        for (int i = 0, j = list.size() - 1; i < j; i++, j--) {
            T tmp = list.get(i);
            list.set(i, list.get(j));
            list.set(j, tmp);
        }
    }
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4));
        reverse(list);
        System.out.println(list);
    }
}

Parâmetros produtores em uma API real

Projetar um addAll que obtenha elementos de qualquer origem compatível exige um limite superior na origem.

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class Main {
    static <T> void addAll(List<T> target, Collection<? extends T> source) {
        for (T t : source) target.add(t);
    }
    public static void main(String[] args) {
        List<Number> nums = new ArrayList<>();
        addAll(nums, List.of(1, 2, 3));
        System.out.println(nums);
    }
}

Parâmetros consumidores em uma API real

Uma API de destino que grava resultados usa um limite inferior no destino, para que os chamadores possam fornecer contêineres de tipos mais abrangentes.

import java.util.ArrayList;
import java.util.List;

public class Main {
    static <T> void repeat(T value, int times, List<? super T> sink) {
        for (int i = 0; i < times; i++) sink.add(value);
    }
    public static void main(String[] args) {
        List<Object> out = new ArrayList<>();
        repeat("hi", 3, out);
        System.out.println(out);
    }
}

Curingas com tipos genéricos aninhados

Tipos genéricos aninhados frequentemente precisam de curingas. Um método que aceita uma lista de qualquer tipo de lista usa List<? extends List<?>>.

import java.util.List;

public class Main {
    static int countAll(List<? extends List<?>> lists) {
        int total = 0;
        for (List<?> inner : lists) total += inner.size();
        return total;
    }
    public static void main(String[] args) {
        System.out.println(countAll(List.of(List.of(1, 2), List.of("a", "b", "c"))));
    }
}

Evitando o excesso de generalização

Nem todo método precisa de curingas. Se os chamadores sempre passarem apenas um tipo, um parâmetro simples será mais claro.

Adicione curingas quando observar chamadores reais sendo impedidos pela invariância, não antecipadamente.

import java.util.List;

public class Main {
    // Simple and clear; no wildcard needed for this internal use
    static String joinStrings(List<String> parts) {
        return String.join(", ", parts);
    }
    public static void main(String[] args) {
        System.out.println(joinStrings(List.of("a", "b", "c")));
    }
}

Documentando a intenção do curinga

Um curinga sinaliza uma intenção. ? extends T diz aos leitores "Eu apenas leio isto". ? super T diz "Eu apenas escrevo isto".

Escolher o curinga correto é uma forma de código autoexplicativo que também impõe o contrato.

import java.util.ArrayList;
import java.util.List;

public class Main {
    // src: read-only producer; dest: write-only consumer
    static <T> void transfer(List<? extends T> src, List<? super T> dest) {
        for (T t : src) dest.add(t);
    }
    public static void main(String[] args) {
        List<Number> dest = new ArrayList<>();
        transfer(List.of(1, 2), dest);
        System.out.println(dest);
    }
}

Uma API de Stack genérica

O famoso exemplo de Stack de Bloch: pushAll consome de um produtor (limite superior), e popAll produz em um consumidor (limite inferior).

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.ArrayList;
import java.util.List;

public class Main {
    static class Stack<E> {
        private final Deque<E> items = new ArrayDeque<>();
        void pushAll(Collection<? extends E> src) { for (E e : src) items.push(e); }
        void popAll(Collection<? super E> dst) { while (!items.isEmpty()) dst.add(items.pop()); }
    }
    public static void main(String[] args) {
        Stack<Integer> s = new Stack<>();
        s.pushAll(List.of(1, 2, 3));
        List<Number> out = new ArrayList<>();
        s.popAll(out);
        System.out.println(out);
    }
}

Verificação rápida

Teste sua intuição para o design de APIs.

Recapitulação

Você aprendeu a projetar APIs com curingas:

  • List<?> para métodos somente para leitura e independentes do tipo.
  • Aplique PECS aos parâmetros para obter flexibilidade.
  • Use o truque privado de captura com um auxiliar quando precisar escrever por meio de um curinga.
  • Evite curingas em tipos de retorno e não generalize em excesso.

Você concluiu o curso sobre curingas e PECS.

public class Main {
    public static void main(String[] args) {
        System.out.println("Wildcards in APIs course complete");
    }
}

Perguntas Frequentes

A aula “Curingas em APIs” é grátis?

Sim — o texto completo de “Curingas em APIs” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Java Academy, atualize para CoddyKit PRO. O curso de Java Academy inclui 4 aulas no total.

O que vou aprender em “Curingas em APIs”?

Projete métodos genéricos flexíveis. Você pratica Java Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Java Academy?

Nenhuma experiência prévia é necessária. Java Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Curingas em APIs”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Java Academy?

Sim. Cada aula de Java Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Curingas com limite superior
  2. Curingas com limite inferior
  3. O princípio PECS
  4. Curingas em APIs
← Voltar para Java Academy