0Pricing
Java Academy · Aula

Curingas com limite superior

? extends T para produtores.

Curingas com limite superior é uma aula grátis de Java Academy no CoddyKit. Esta é a aula 1 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.

Os genéricos são invariantes

Em Java, os genéricos são invariantes. Uma List<Integer> não é uma List<Number>, embora Inteiro seja um Número.

Isso surpreende quem está começando, mas mantém o sistema de tipos seguro. Os curingas relaxam essa restrição com segurança.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> ints = List.of(1, 2, 3);
        // List<Number> nums = ints; // would NOT compile
        System.out.println("List<Integer> is not List<Number>");
        System.out.println(ints);
    }
}

O curinga com limite superior

? extends T é um curinga com limite superior. Isso significa algum tipo desconhecido que seja o tipo indicado ou um subtipo dele.

List<? extends Number> pode fazer referência a uma List<Integer>, a uma List<Double> ou a uma List<Number>.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> a = List.of(1, 2, 3);
        List<? extends Number> b = List.of(1.5, 2.5);
        System.out.println(a.get(0));
        System.out.println(b.get(0));
    }
}

Lendo de um produtor

Com ? extends Number, você pode ler elementos com segurança como Number.

Seja qual for o tipo real dos elementos, é garantido que ele é pelo menos um Número; portanto, a leitura é segura quanto aos tipos.

import java.util.List;

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

Não é possível escrever nele

Aqui está o ponto importante: você não pode adicionar elementos a uma lista ? extends Number (exceto nulo).

O compilador não conhece o tipo exato do elemento. Ela poderia ser uma List<Integer>, portanto adicionar um Double seria inseguro.

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

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Integer>();
        // list.add(1);   // does NOT compile
        // list.add(1.5); // does NOT compile
        System.out.println("Cannot add to ? extends Number");
        System.out.println("size = " + list.size());
    }
}

Por que não é permitido escrever

Imagine se isso fosse permitido. Você poderia passar uma List<Integer> como List<? extends Number> e depois adicionar um Double.

Mais tarde, o código que lesse a lista como List<Integer> obteria uma ClassCastException. O compilador evita isso proibindo operações de escrita.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Conceptual: this is why add is blocked
        System.out.println("Writing a Double into a List<Integer> would corrupt it");
        System.out.println("so ? extends bans all adds");
    }
}

Caso de uso de produtor

Use ? extends T quando um método apenas produz valores para você, ou seja, quando apenas os lê.

Um método que copia elementos de uma coleção de origem é um exemplo perfeito.

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

public class Main {
    static List<Number> copyOut(List<? extends Number> source) {
        List<Number> dest = new ArrayList<>();
        for (Number n : source) dest.add(n);
        return dest;
    }
    public static void main(String[] args) {
        System.out.println(copyOut(List.of(1, 2, 3)));
    }
}

Parâmetros de tipo limitados versus curingas

Não confunda <T extends Number> (um parâmetro de tipo limitado) com ? extends Number (um curinga).

  • Parâmetro de tipo: dá nome ao tipo para que ele seja usado em todo o método.
  • Curinga: um subtipo desconhecido e anônimo, usado quando não é necessário dar um nome a ele.
import java.util.List;

public class Main {
    static <T extends Number> T first(List<T> list) { return list.get(0); }
    public static void main(String[] args) {
        Integer i = first(List.of(10, 20));
        System.out.println(i);
    }
}

Vários limites

Um parâmetro de tipo pode ter vários limites usando &, como em <T extends Number & Comparable<T>>.

Os curingas aceitam apenas um único limite superior, portanto vários limites exigem um parâmetro de tipo nomeado.

import java.util.List;

public class Main {
    static <T extends Number & Comparable<T>> T max(List<T> list) {
        T best = list.get(0);
        for (T t : list) if (t.compareTo(best) > 0) best = t;
        return best;
    }
    public static void main(String[] args) {
        System.out.println(max(List.of(3, 9, 1, 7)));
    }
}

Curingas com Collections.max

O JDK usa limites superiores amplamente. Por exemplo, ao somar valores ou percorrer uma coleção somente para leitura.

Aqui encontramos o maior elemento de uma lista tipada como produtora.

import java.util.List;

public class Main {
    static double largest(List<? extends Number> nums) {
        double max = Double.NEGATIVE_INFINITY;
        for (Number n : nums) max = Math.max(max, n.doubleValue());
        return max;
    }
    public static void main(String[] args) {
        System.out.println(largest(List.of(4, 2, 9, 1)));
        System.out.println(largest(List.of(0.5, 9.9, 3.3)));
    }
}

Nulo é o único valor gravável

A única coisa que você pode adicionar a uma coleção ? extends T é null, porque nulo pode ser atribuído a qualquer tipo de referência.

Isso raramente é útil, mas explica por que add não é completamente proibido, apenas na prática.

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

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Number>();
        // Only null is assignable; demonstrated conceptually
        System.out.println("Only null could be added; we avoid that");
        System.out.println("size = " + list.size());
    }
}

Ler como Object sempre funciona

Como todo tipo é um subtipo de Object, você sempre pode ler os elementos de qualquer lista com curinga como Object.

Com um limite superior de Number, você ainda pode lê-los diretamente como um tipo numérico.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> nums = List.of(1, 2.0, 3L);
        for (Object o : nums) System.out.println(o.getClass().getSimpleName());
    }
}

Verificação rápida

Teste sua compreensão dos limites superiores.

Recapitulação

Você aprendeu sobre curingas com limite superior:

  • ? extends T significa um subtipo desconhecido de T.
  • Você pode ler elementos como T (um produtor).
  • Você não pode adicionar elementos que não sejam nulos.
  • Os tipos genéricos são invariantes, e os curingas relaxam essa regra com segurança.

A seguir, a imagem espelhada: curingas com limite inferior.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> nums = List.of(1, 2, 3);
        System.out.println("Upper bound recap: read " + nums.get(0));
    }
}

Perguntas Frequentes

A aula “Curingas com limite superior” é grátis?

Sim — o texto completo de “Curingas com limite superior” é 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 com limite superior”?

? extends T para produtores. 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 1 de 4.

Quanto tempo leva a aula “Curingas com limite superior”?

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