0Pricing
Java Academy · Lección

Wildcards en APIs

Diseñe métodos genéricos flexibles

Wildcards en APIs es una lección gratuita de Java Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Java Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Java Academy incluye 4 lecciones en total.

Diseñar métodos flexibles

Los comodines son principalmente una herramienta para diseñadores de API. Un comodín bien ubicado permite que quienes llaman al método pasen más tipos sin conversiones explícitas.

El objetivo es ofrecer la máxima flexibilidad a quienes llaman al método y mantener la seguridad de tipos completa en el interior.

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

Comodines sin límite

List<?> es un comodín sin límite: una lista de algún tipo desconocido.

Úselo cuando a su método no le importe el tipo de los elementos, por ejemplo, para contar elementos o comprobar si la lista está vacía.

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<?> frente a List<Object>

Son tipos diferentes. List<Object> solo acepta una lista declarada como Object. List<?> acepta una lista de cualquier tipo.

Puede pasar una List<String> a un parámetro List<?>, pero no a uno de tipo 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
    }
}

Cuándo es más claro usar un parámetro de tipo

Si necesita referirse al tipo de los elementos más de una vez, un parámetro de tipo con nombre resulta más claro que un comodín.

Compare void swap(List<?> l, int i, int j) (que necesita un auxiliar privado) con una versión genérica más limpia.

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

El truco de captura mediante un auxiliar

Si una API pública debe usar List<?>, puede delegar en un auxiliar genérico privado para capturar el comodín.

El auxiliar asigna el nombre T al tipo, de modo que puede volver a escribir en la 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 productores en una API real

Diseñar un método addAll que extraiga elementos de cualquier origen compatible requiere un límite superior en el origen.

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 en una API real

Una API de destino que escribe resultados utiliza un límite inferior en el destino, para que quienes la llaman puedan proporcionar contenedores más amplios.

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

Comodines con genéricos anidados

Los genéricos anidados suelen necesitar comodines. Un método que acepta una lista de cualquier tipo de lista utiliza 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"))));
    }
}

Evitar el exceso de generificación

No todos los métodos necesitan comodines. Si quienes llaman al método solo van a pasar un tipo, un parámetro simple resulta más claro.

Añada comodines cuando observe que la invariancia realmente impide trabajar a quienes llaman al método, no de forma preventiva.

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

Documentar la intención de los comodines

Un comodín comunica una intención. ? extends T indica «solo leo esto». ? super T indica «solo escribo esto».

Elegir el comodín adecuado es una forma de escribir código autodocumentado que además hace cumplir el 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);
    }
}

Una API de pila genérica

El famoso ejemplo de pila de Bloch: pushAll consume de un productor (extends) y popAll produce en un consumidor (super).

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

Comprobación rápida

Compruebe su intuición para diseñar API.

Resumen

Ha aprendido a diseñar API con comodines:

  • List<?> para métodos de solo lectura independientes del tipo.
  • Aplique PECS a los parámetros para obtener flexibilidad.
  • Use el truco de captura mediante un auxiliar privado cuando necesite escribir a través de un comodín.
  • Evite los comodines en los tipos de retorno y no abuse de la generificación.

Ha completado el curso sobre comodines y PECS.

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

Preguntas frecuentes

¿La lección «Wildcards en APIs» es gratis?

Sí — el texto completo de «Wildcards en APIs» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Java Academy, actualiza a CoddyKit PRO. El curso de Java Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Wildcards en APIs»?

Diseñe métodos genéricos flexibles Practicas Java Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Java Academy?

No se requiere experiencia previa. Java Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Wildcards en APIs»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Java Academy?

Sí. Cada lección de Java Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Wildcards con límite superior
  2. Wildcards con límite inferior
  3. El principio PECS
  4. Wildcards en APIs
← Volver a Java Academy