Pattern matching en switch (Java 21)

Avanzado
Java
Java
Actualizado: 18/04/2026

Qué es pattern matching en switch

El pattern matching para switch, estabilizado en Java 21 (tras varias rondas en preview desde Java 17), amplía el switch para que cada case pueda comprobar un patrón de tipo y extraer la variable en un solo paso. Elimina cadenas de if-else instanceof y las convierte en código declarativo.

Antes: cadena de if-else instanceof

static String describir(Object obj) {
    String resultado;
    if (obj instanceof Integer i) {
        resultado = "entero: " + i;
    } else if (obj instanceof Long l) {
        resultado = "long: " + l;
    } else if (obj instanceof Double d) {
        resultado = "double: " + d;
    } else if (obj instanceof String s) {
        resultado = "texto: " + s;
    } else if (obj == null) {
        resultado = "nulo";
    } else {
        resultado = "desconocido";
    }
    return resultado;
}

Con pattern matching en switch

static String describir(Object obj) {
    return switch (obj) {
        case Integer i -> "entero: " + i;
        case Long l -> "long: " + l;
        case Double d -> "double: " + d;
        case String s -> "texto: " + s;
        case null -> "nulo";
        default -> "desconocido";
    };
}

Más legible, sin asignaciones intermedias, compilador ayuda con exhaustividad.

Type patterns en case

La sintaxis case Tipo variable combina:

  1. Comprueba que el valor es del tipo indicado.
  2. Lo binding: extrae el valor tipado en la variable.
  3. Usa la variable en el lado derecho.
Object dato = obtenerDato();
int doble = switch (dato) {
    case Integer i -> i * 2;
    case String s -> Integer.parseInt(s) * 2;
    default -> 0;
};

Guardas con when

Para añadir condiciones sobre el valor además del tipo, usa when:

static String clasificar(Object obj) {
    return switch (obj) {
        case Integer i when i < 0 -> "entero negativo";
        case Integer i when i == 0 -> "cero";
        case Integer i when i > 100 -> "entero grande: " + i;
        case Integer i -> "entero normal: " + i;
        case String s when s.isBlank() -> "texto vacío";
        case String s -> "texto: " + s;
        default -> "otro";
    };
}

Las guardas se evalúan en el orden textual, y el primer patrón que coincide y cuya guarda da true es el elegido. El compilador detecta casos dominados (cubiertos por un patrón anterior) como error.

Caso null explícito

Antes de Java 21, pasar null a un switch lanzaba NullPointerException. Ahora puedes manejarlo de forma directa:

String saludar(String nombre) {
    return switch (nombre) {
        case null -> "Hola, extraño";
        case "admin" -> "Hola, admin";
        case String s when s.isBlank() -> "Hola, anónimo";
        case String s -> "Hola, " + s;
    };
}

Si no incluyes case null y el valor es null, se lanza NullPointerException (preserva compatibilidad con comportamiento anterior).

Combinaciones permitidas: case null, default -> ... trata null como el caso por defecto. case null, Tipo t -> ... permite cubrir null junto a un patrón tipado.

Exhaustividad con sealed

El ventaja del pattern matching en switch aparece con sealed interface y record. El compilador verifica que cubres todas las posibilidades:

sealed interface Forma permits Circulo, Cuadrado, Triangulo {}
record Circulo(double radio) implements Forma {}
record Cuadrado(double lado) implements Forma {}
record Triangulo(double base, double altura) implements Forma {}

static double area(Forma forma) {
    return switch (forma) {
        case Circulo c -> Math.PI * c.radio() * c.radio();
        case Cuadrado q -> q.lado() * q.lado();
        case Triangulo t -> 0.5 * t.base() * t.altura();
        // ¡no se necesita default! El compilador verifica que Forma solo tiene 3 subtipos
    };
}

Si añades Pentagono a permits y olvidas actualizar el switch, el código no compila. Es refactorización asistida por el compilador.

Switch statement (sin devolver valor)

Pattern matching también funciona como sentencia:

void procesar(Object evento) {
    switch (evento) {
        case ClickEvento c -> log.info("clic en " + c.posicion());
        case ScrollEvento s -> log.info("scroll " + s.delta());
        case KeyEvento k -> log.info("tecla " + k.codigo());
        case null -> log.warn("evento nulo");
        default -> log.warn("evento desconocido: " + evento);
    }
}

Orden de los patrones

El orden importa. Java rechaza patrones dominados (inalcanzables):

// ERROR de compilación: el segundo case nunca se alcanza
switch (obj) {
    case Number n -> "número: " + n;
    case Integer i -> "entero: " + i; // Integer es Number, ya cubierto arriba
}

Pon siempre los patrones más específicos antes de los generales.

Patrones con condiciones combinadas

Puedes combinar patrones con guardas complejas:

record Moneda(String codigo, BigDecimal cantidad) {}

String categorizar(Moneda m) {
    return switch (m) {
        case Moneda moneda when moneda.cantidad().signum() < 0 -> "deuda";
        case Moneda moneda when moneda.cantidad().signum() == 0 -> "sin saldo";
        case Moneda moneda when "EUR".equals(moneda.codigo()) -> "euros: " + moneda.cantidad();
        case Moneda moneda -> "otra divisa";
    };
}

Ejemplos de uso profesional

Procesamiento de eventos tipados

sealed interface Evento permits ClickEvento, TecladoEvento, ScrollEvento {}
record ClickEvento(int x, int y, int boton) implements Evento {}
record TecladoEvento(int codigo, boolean shift) implements Evento {}
record ScrollEvento(int delta) implements Evento {}

String describir(Evento evento) {
    return switch (evento) {
        case ClickEvento c when c.boton() == 1 -> "clic izquierdo en (%d,%d)".formatted(c.x(), c.y());
        case ClickEvento c when c.boton() == 2 -> "clic derecho en (%d,%d)".formatted(c.x(), c.y());
        case ClickEvento c -> "clic boton " + c.boton();
        case TecladoEvento t when t.shift() -> "SHIFT + código " + t.codigo();
        case TecladoEvento t -> "código " + t.codigo();
        case ScrollEvento s -> "scroll " + s.delta();
    };
}

Visitor moderno

El patrón Visitor tradicional se vuelve innecesario: pattern matching sobre una sealed hierarchy lo reemplaza directamente con la ventaja de no tener que modificar las clases hoja.

sealed interface Expresion permits Literal, Suma, Multiplicacion {}
record Literal(int valor) implements Expresion {}
record Suma(Expresion izq, Expresion der) implements Expresion {}
record Multiplicacion(Expresion izq, Expresion der) implements Expresion {}

int evaluar(Expresion e) {
    return switch (e) {
        case Literal(int v) -> v;
        case Suma(var a, var b) -> evaluar(a) + evaluar(b);
        case Multiplicacion(var a, var b) -> evaluar(a) * evaluar(b);
    };
}

(Nota: Literal(int v), Suma(var a, var b) son record patterns, que deconstruyen los records en el propio case: se tratan en su propio tutorial.)

Diferencias con switch tradicional

| Aspecto | Switch tradicional | Pattern matching switch | |---------|--------------------|-------------------------| | Tipo del selector | Primitivos, enum, String | Cualquier objeto | | Comprobar tipo | No | Sí (case Tipo t) | | Guardas | No | Sí (when) | | Null handling | NPE implícito | case null explícito | | Exhaustividad verificada | No | Sí con sealed | | Código típico | Solo igualdad simple | Polimorfismo tipado |

Limitaciones

  • Los patrones deben ser totales (con default o cobertura de sealed).
  • No puedes mezclar case con valores constantes (case 1, 2) y type patterns (case Integer i) en el mismo switch (o todo constantes o todo patrones + tipo compatible).
  • El compilador verifica dominancia: pon casos específicos primero.

Resumen

Pattern matching en switch es, junto con records y sealed classes, la pieza central del Java moderno para modelado de tipos. Convierte jerarquías de clases en código expresivo, seguro y verificado en compilación. Cualquier equipo Java profesional en 2026 debería usarlo sistemáticamente.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Java es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Java

Explora más contenido relacionado con Java y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Usar pattern matching en switch expressions y statements. Aplicar type patterns (case Tipo x). Añadir guardas con when. Combinar con sealed interface para exhaustividad verificada en compilación. Manejar el caso null directamente en switch. Sustituir cadenas de if-else instanceof por código declarativo.