Saltar a contenido

11. assert y aserciones

UD3 — Control de Flujo y Depuración · RA3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje

IES Thiar — Pilar de la Horadada (Alicante) · Curso 2026-2027

Aspecto Valor
Resultado de aprendizaje RA: 3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje.
Criterios de evaluación a los que contribuye esta sección CE 3.6 (prueba y depura los programas)
CE 3.9 (utiliza aserciones para la detección y corrección de errores durante la fase de desarrollo).
Marco normativo RD 405/2023 · RD 450/2010 · RD 659/2023
Tecnología base Java 25 LTS (OpenJDK Temurin 25) — IntelliJ IDEA Community Edition
Horas estimadas ~2 h (de las 30 h de la UD3).

Requisitos previos (lo que ya sabes): todas las estructuras de control de UD3: condiciones booleanas (sección 1); if / else if / else y early return (sección 2); switch expression (sección 3); while y do-while (sección 4); arrays unidimensionales (sección 5); for clásico y utilidades de Arrays (sección 6); for-each (sección 7); bucles anidados, matrices int[][] y break/continue/return (sección 8); try-catch-finally y manejo de excepciones predefinidas (sección 9); depurador de IntelliJ IDEA con breakpoints, Step Over/Into/Out y Evaluate Expression (sección 10). Tipos primitivos, var, String, final para constantes, IO.readln(), Integer.parseInt / Double.parseDouble (de UD1).

Lo que NO verás aquí (se reserva para secciones/unidades posteriores): test de repaso (sección 12 de UD3), ejercicios integradores (sección 13 de UD3). Tampoco verás testing con JUnit (UD2 intro, UD4 profundización), ni logging con SLF4J/Logback (UD5 / profesional), ni design-by-contract con librerías externas (fuera del módulo). Aquí solo vemos assert, la herramienta nativa de Java para aserciones.


11.1 ¿De qué va esta sección? — Alarmas automáticas en tu código

En la sección 10 aprendiste a usar el depurador para investigar bugs cuando ocurren: pones un breakpoint, ejecutas línea a línea, inspeccionas variables y encuentras el error. Es una técnica reactiva: reaccionas cuando algo ya va mal.

Pero hay otra técnica preventiva: poner alarmas automáticas en tu código que suenan si algo inesperado ocurre durante la ejecución. Esas alarmas se llaman aserciones (assert), y son la herramienta que verás en esta sección.

Una aserción es una afirmación que el programador considera siempre cierta en un punto del programa. Si resulta ser falsa, Java lanza un error (AssertionError) y el programa se detiene, avisándote de que algo que dabas por seguro... no lo era.

Cubriremos, en este orden:

  1. Qué es una aserción y para qué sirve.
  2. Sintaxis de assert: con y sin mensaje.
  3. Activar aserciones con -ea: por qué Java las desactiva por defecto.
  4. assert vs. excepciones: cuándo usar cada una.
  5. Precondiciones y postcondiciones: los dos usos principales.
  6. Invariantes de bucle: aserciones dentro de bucles.
  7. Trampas comunes con assert.
  8. Ejemplos resueltos paso a paso.

Analogía del detector de humo

El depurador es como un inspector que entra con una linterna y busca el problema. assert es como un detector de humo: está instalado en puntos estratégicos y, si detecta humo (una condición falsa que no debería ocurrir), suena la alarma automáticamente. No necesitas estar mirando: la alarma te avisa.

Vamos a empezar por la sintaxis, que es de lo más simple que hay en Java.


11.2 Sintaxis de assert

11.2.1 Forma básica (sin mensaje)

assert condicion;

Si condicion es true, no pasa nada: el programa continúa. Si condicion es false, Java lanza AssertionError y el programa se detiene.

1
2
3
4
int edad = Integer.parseInt(IO.readln().trim());

assert edad >= 0;   // Si edad es negativo, salta AssertionError
// Si edad >= 0, el programa continúa normalmente

11.2.2 Forma con mensaje

assert condicion : mensaje;

Si condicion es false, Java lanza AssertionError con el mensaje que especificas. El mensaje se muestra en la traza y te ayuda a entender qué falló.

1
2
3
4
int edad = Integer.parseInt(IO.readln().trim());

assert edad >= 0 : "La edad no puede ser negativa, pero era " + edad;
// Si edad = -5, salta: AssertionError: La edad no puede ser negativa, pero era -5

11.2.3 ¿Qué puede ser la condición?

La condición tiene que ser una expresión booleana: cualquier cosa que se evalúe a true o false. Puede usar variables, métodos, operadores, etc.

1
2
3
4
5
assert i >= 0;
assert i < arr.length;
assert arr != null;
assert suma == esperado;
assert !lista.isEmpty();   // cuando veas colecciones en UD6

11.2.4 ¿Qué puede ser el mensaje?

El mensaje es opcional. Si lo pones, puede ser cualquier expresión que devuelva un valor (normalmente un String, pero también puede ser un número u objeto; Java llama a toString() automáticamente).

1
2
3
assert i >= 0 : "i debería ser >= 0, pero era " + i;
assert arr != null : "El array no debería ser null en este punto";
assert suma == esperado : "Suma = " + suma + ", esperado = " + esperado;

El mensaje se construye con concatenación (con +), igual que en IO.println. Es muy útil incluir los valores reales para diagnosticar el problema.

11.2.5 Tabla resumen de la sintaxis

Forma Sintaxis Si es true Si es false
Sin mensaje assert condicion; Continúa. Lanza AssertionError sin mensaje.
Con mensaje assert condicion : mensaje; Continúa. Lanza AssertionError con el mensaje.

11.3 Activar aserciones con -ea

11.3.1 El gran detalle: assert está desactivado por defecto

Java trae assert desde la versión 1.4 (2004), pero por defecto está desactivado. Si escribes assert edad >= 0; y ejecutas el programa normalmente, no pasa nada: la aserción se ignora completamente. Ni siquiera se evalúa la condición.

¿Por qué? Por rendimiento: en producción, no quieres que el programa pierda tiempo comprobando aserciones en cada línea. Las aserciones son para desarrollo, no para producción. Java las desactiva por defecto y te da la opción de activarlas cuando las necesitas.

11.3.2 Cómo activarlas: el flag -ea

Para activar las aserciones, hay que pasar el flag -ea (de enable assertions) a la JVM al ejecutar el programa.

Desde la línea de comandos:

java -ea MiPrograma

Desde IntelliJ IDEA:

  1. Ve a Run → Edit Configurations... (o el desplegable de configuraciones de ejecución, a la izquierda del botón Run).
  2. Selecciona tu configuración de ejecución (la clase main).
  3. En el campo VM options, escribe -ea.
  4. Pulsa OK.
  5. Ahora, cuando ejecutes con Shift+F10 o Shift+F9, las aserciones estarán activadas.
Use mouse to pan and zoom
flowchart TD
    A["Run → Edit Configurations"] --> B["Selecciona la configuración"]
    B --> C["VM options: escribe -ea"]
    C --> D["OK"]
    D --> E["Ahora assert funciona al ejecutar"]

Si no activas -ea, assert no hace nada

Es la trampa nº 1 con assert. Escribes la aserción, ejecutas el programa, y parece que no pasa nada, aunque la condición sea falsa. Eso es porque -ea no está activado. Siempre que vayas a probar aserciones, verifica que -ea está en VM options.

11.3.3 Verificar que las aserciones están activadas

La forma más simple de comprobarlo: pon una aserción que sabes que es falsa y ejecuta el programa.

1
2
3
4
5
6
7
public class PruebaAssert {

    public static void main(String[] args) {
        assert false : "Si ves esto, las aserciones están activadas.";
        IO.println("Si ves esto, las aserciones NO están activadas.");
    }
}
  • Si ves AssertionError: Si ves esto, las aserciones están activadas. → -ea está activado.
  • Si ves Si ves esto, las aserciones NO están activadas. → -ea no está activado. Ve a Edit Configurations y añádelo.

11.3.4 Desactivar aserciones para producción

Cuando termines de desarrollar y quieras ejecutar el programa "en serio" (sin el overhead de las aserciones), simplemente quita -ea de las VM options. Las aserciones se ignoran y el programa corre a máxima velocidad, como si no existieran.

11.3.5 -ea y -da

Flag Significado Efecto
-ea Enable Assertions Activa todas las aserciones.
-da Disable Assertions Desactiva todas las aserciones (comportamiento por defecto).
-ea:jams.programacion.ud3 Enable Assertions en un paquete Activa solo las aserciones de las clases del paquete jams.programacion.ud3.
-ea:MiClase Enable Assertions en una clase Activa solo las aserciones de una clase concreta.

La forma más habitual es -ea sin argumentos: activa todas las aserciones de todo el programa.


11.4 assert vs. excepciones: cuándo usar cada una

Esta es la decisión más importante de esta sección. Tienes dos herramientas para señalar errores:

  • assert: para cosas que el programador sabe que deben ser ciertas. Si fallan, es un bug.
  • throw new Excepcion: para cosas que el usuario o el entorno pueden provocar. Si ocurren, hay que manejarlas.

11.4.1 Tabla comparativa

Característica assert throw new Excepcion
¿Para quién es? El programador (contratos internos). El usuario / el entorno (errores esperados).
¿Qué significa si falla? Hay un bug en el código. Algo inesperado ocurrió (entrada incorrecta, fichero no encontrado...).
¿Está activado por defecto? No (hay que poner -ea). Sí (siempre funciona).
¿Se debería ver en producción? No (se ignora). Sí (hay que manejarlo con try-catch).
¿Se puede capturar con catch? Técnicamente sí, pero no se debe. Sí, es para eso.
¿Qué lanza? AssertionError (no es Exception, es Error). Exception o subclase.
Ejemplo típico "Esta variable no debería ser null aquí." "El usuario escribió 'abc' donde esperaba un número."

11.4.2 La regla de oro

"assert para bugs del programador, excepciones para errores del usuario."

Si la condición puede fallar por una entrada incorrecta del usuario o un estado del entorno (fichero no existe, red caída), usa throw new Excepcion y captúrala con try-catch.

Si la condición nunca debería fallar si el código está bien escrito, usa assert. Si falla, significa que hay un bug que debes corregir.

11.4.3 Ejemplos de cuándo usar cada uno

Situación Herramienta Razón
La edad que el usuario escribe es negativa. throw new IllegalArgumentException El usuario puede escribir mal; hay que manejarlo.
Después de ordenar un array, arr[i] <= arr[i+1] no se cumple. assert arr[i] <= arr[i+1] Si el algoritmo de ordenación es correcto, esto siempre es cierto. Si falla, hay un bug en el algoritmo.
El usuario escribe "abc" donde esperábamos un número. try-catch con NumberFormatException El usuario puede equivocarse; hay que volver a pedir.
Una variable interna indice debería estar entre 0 y 99 en este punto. assert indice >= 0 && indice < 100 Si no lo está, es un bug de lógica interna.
Un fichero no existe. throw new IOException (checked) El entorno puede fallar; hay que capturarlo.
El resultado de un cálculo debería ser positivo. assert resultado >= 0 Si es negativo, hay un bug en el cálculo.

11.4.4 Nunca captures AssertionError

AssertionError hereda de Error, no de Exception. Aunque técnicamente puedes capturarlo con catch (AssertionError e), no debes hacerlo. Si una aserción falla, el programa debe detenerse: hay un bug que arreglar, no un error que manejar.

// MAL: capturar AssertionError
try {
    assert condicion;
} catch (AssertionError e) {
    // No hagas esto. Si assert falla, hay un bug: arréglalo.
}

// BIEN: dejar que AssertionError detenga el programa
assert condicion;
// Si falla, el programa se detiene y ves el stack trace

11.5 Precondiciones y postcondiciones

Los dos usos principales de assert son las precondiciones y las postcondiciones. Vamos a verlos.

11.5.1 Precondiciones: lo que debe ser cierto ANTES

Una precondición es una condición que debe ser cierta antes de ejecutar un bloque de código. Si no se cumple, el bloque no debería ejecutarse (o su resultado sería incorrecto).

En UD3 no tienes métodos propios (los verás en UD4), pero puedes poner precondiciones al inicio de un bloque dentro del main:

// Precondición: el array no debe ser null ni estar vacío
int[] numeros = obtenerArray();   // supongamos que esto devuelve un array

assert numeros != null : "El array no debería ser null";
assert numeros.length > 0 : "El array no debería estar vacío";

// A partir de aquí, sabemos que numeros no es null y tiene al menos 1 elemento
int max = numeros[0];
for (int i = 1; i < numeros.length; i++) {
    if (numeros[i] > max) {
        max = numeros[i];
    }
}
IO.println("Máximo: " + max);

Las dos aserciones al principio garantizan que, si el array es null o está vacío, el programa se detiene antes de intentar acceder a numeros[0] (que lanzaría NullPointerException o ArrayIndexOutOfBoundsException).

11.5.2 Postcondiciones: lo que debe ser cierto DESPUÉS

Una postcondición es una condición que debe ser cierta después de ejecutar un bloque de código. Si no se cumple, el bloque hizo algo incorrecto.

// Postcondición: después de calcular el máximo, max debe ser >= que todos los elementos
int[] numeros = {5, 8, 3, 9, 2};
int max = numeros[0];
for (int i = 1; i < numeros.length; i++) {
    if (numeros[i] > max) {
        max = numeros[i];
    }
}

// Postcondición: max debería ser el máximo del array
// Verificamos que max >= cada elemento
for (int i = 0; i < numeros.length; i++) {
    assert max >= numeros[i] : "max debería ser >= que numeros[" + i + "], pero max=" + max + " y numeros[" + i + "]=" + numeros[i];
}

IO.println("Máximo: " + max);

Esta postcondición comprueba que, después del bucle, max es mayor o igual que cada elemento del array. Si no lo es, hay un bug en el algoritmo de búsqueda del máximo.

11.5.3 Diferencia visual

Use mouse to pan and zoom
flowchart TD
    subgraph Pre ["Precondición — antes del bloque"]
        A["assert condicion_antes"] --> B["Si falla: el bloque no debería ejecutarse"]
    end
    subgraph Bloque ["Bloque de código"]
        B --> C["Ejecutar el bloque"]
    end
    subgraph Post ["Postcondición — después del bloque"]
        C --> D["assert condicion_despues"]
        D --> E["Si falla: el bloque hizo algo incorrecto"]
    end

11.5.4 Cuándo usar cada una

Tipo Cuándo se pone Qué verifica
Precondición Al inicio de un bloque. Que las entradas son válidas antes de procesarlas.
Postcondición Al final de un bloque. Que las salidas son correctas después de procesar.

En UD4, cuando veas métodos propios, las precondiciones van al inicio del método (antes de la lógica) y las postcondiciones van al final (antes del return). Aquí, en UD3, las pones al inicio y al final de bloques dentro del main.


11.6 Invariantes de bucle

Un invariante de bucle es una condición que debería ser cierta al inicio de cada iteración del bucle. Se puede comprobar con assert al principio del cuerpo del bucle.

11.6.1 Ejemplo: suma de un array

int[] numeros = {10, 20, 30, 40, 50};
int suma = 0;

for (int i = 0; i < numeros.length; i++) {
    // Invariante: suma debería ser la suma de numeros[0..i-1]
    int sumaEsperada = 0;
    for (int j = 0; j < i; j++) {
        sumaEsperada += numeros[j];
    }
    assert suma == sumaEsperada : "suma=" + suma + " pero esperada=" + sumaEsperada;

    suma = suma + numeros[i];
}

Este invariante comprueba que, al inicio de cada iteración i, suma contiene la suma de los elementos de 0 a i-1. Si no es así, hay un bug en la lógica del bucle.

11.6.2 Cuándo usar invariantes

Los invariantes de bucle son útiles pero costosos: en este ejemplo, por cada iteración del bucle principal, hacemos otro bucle completo para verificar la suma. En producción, esto sería inaceptable (duplica el tiempo de ejecución). Pero en desarrollo, es una forma muy potente de detectar bugs en algoritmos complejos.

Regla del profesor:

Usa invariantes de bucle en algoritmos complejos donde un bug en una iteración se propagaría a las siguientes. Para bucles simples (sumar, buscar máximo), no hace falta: el algoritmo es suficientemente simple para confiar en él sin verificación.


11.7 Trampas comunes con assert

11.7.1 Trampa 1: olvidar activar -ea

Ya la vimos en 11.3.2. Solución: siempre que vayas a probar aserciones, verifica que -ea está en VM options.

11.7.2 Trampa 2: usar assert para validación de entrada

1
2
3
// MAL: assert para validar entrada del usuario
int edad = Integer.parseInt(IO.readln().trim());
assert edad >= 0 && edad <= 120 : "Edad fuera de rango";

Si el usuario escribe 200, la aserción falla... pero solo si -ea está activado. Si no lo está (p. ej., en producción), la aserción se ignora y el programa continúa con edad = 200, lo que puede causar bugs posteriores.

Solución: usa if para validar entrada del usuario, no assert.

1
2
3
4
5
6
// BIEN: if para validar entrada del usuario
int edad = Integer.parseInt(IO.readln().trim());
if (edad < 0 || edad > 120) {
    IO.println("Edad fuera de rango.");
    return;
}

11.7.3 Trampa 3: efectos secundarios en la condición

// MAL: la condición tiene un efecto secundario
assert lista.remove(elemento);   // si -ea está desactivado, remove no se ejecuta

Si -ea está desactivado, la condición no se evalúa y lista.remove(elemento) no se ejecuta. El programa se comporta de forma distinta según si las aserciones están activadas o no.

Solución: la condición de assert debe ser pura: solo comprobar, no modificar nada.

1
2
3
// BIEN: separar la acción de la comprobación
boolean eliminado = lista.remove(elemento);
assert eliminado : "No se pudo eliminar el elemento";

11.7.4 Trampa 4: capturar AssertionError

1
2
3
4
5
6
// MAL: capturar AssertionError
try {
    assert condicion;
} catch (AssertionError e) {
    IO.println("Algo falló.");
}

Solución: nunca captures AssertionError. Si una aserción falla, hay un bug: déjalo explotar, lee la traza y arréglalo.

11.7.5 Trampa 5: confundir assert con if

// MAL: usar assert como if (no ejecuta código alternativo)
assert edad >= 18;
// No hay "else" para assert: si falla, el programa se detiene

// BIEN: usar if cuando necesitas una alternativa
if (edad >= 18) {
    IO.println("Mayor de edad.");
} else {
    IO.println("Menor de edad.");
}

assert no tiene "rama falsa". No puedes ejecutar código alternativo si la aserción falla: el programa se detiene. Si necesitas una alternativa, usa if.

11.7.6 Resumen de trampas

Trampa Síntoma Solución
Olvidar -ea assert no hace nada. Activar -ea en VM options.
assert para entrada del usuario Se ignora en producción. Usar if para validación de entrada.
Efectos secundarios en la condición Comportamiento distinto con/sin -ea. La condición debe ser pura.
Capturar AssertionError Oculta el bug. Nunca captures AssertionError.
assert como if No hay alternativa. Usar if cuando necesitas rama falsa.

11.8 Ejemplos resueltos paso a paso

Cinco ejemplos que integran todo lo aprendido.

11.8.1 Ejemplo 1: precondición en un array

public class PrecondicionArray {

    public static void main(String[] args) {

        int[] numeros = {10, 20, 30, 40, 50};

        // Precondición: el array no debe ser null ni estar vacío
        assert numeros != null : "El array no debería ser null";
        assert numeros.length > 0 : "El array no debería estar vacío";

        // Calcular el máximo (sabemos que el array tiene al menos 1 elemento)
        int max = numeros[0];
        for (int i = 1; i < numeros.length; i++) {
            if (numeros[i] > max) {
                max = numeros[i];
            }
        }

        IO.println("Máximo: " + max);
    }
}

Puntos clave:

  1. Dos precondiciones al inicio: numeros != null y numeros.length > 0.
  2. Si cualquiera falla, AssertionError detiene el programa antes de acceder a numeros[0].
  3. Recuerda: activa -ea para que las aserciones funcionen.

11.8.2 Ejemplo 2: postcondición después de un cálculo

public class PostcondicionSuma {

    public static void main(String[] args) {

        int[] numeros = {1, 2, 3, 4, 5};
        int suma = 0;

        for (int i = 0; i < numeros.length; i++) {
            suma = suma + numeros[i];
        }

        // Postcondición: la suma del 1 al 5 es 15
        assert suma == 15 : "La suma debería ser 15, pero es " + suma;

        IO.println("Suma: " + suma);
    }
}

Puntos clave:

  1. Postcondición al final del bucle: suma == 15.
  2. Si el bucle tiene un bug (p. ej., <= en vez de <), la suma será incorrecta y la aserción fallará.
  3. El mensaje incluye el valor real (suma) para diagnosticar.

11.8.3 Ejemplo 3: assert vs. if — el caso de la edad

public class AssertVsIf {

    public static void main(String[] args) {

        IO.print("Edad: ");
        int edad = Integer.parseInt(IO.readln().trim());

        // MAL: assert para validar entrada (se ignora sin -ea)
        // assert edad >= 0 && edad <= 120 : "Edad fuera de rango";

        // BIEN: if para validar entrada del usuario
        if (edad < 0 || edad > 120) {
            IO.println("Edad fuera de rango. Debe estar entre 0 y 120.");
            return;
        }

        // Ahora sí podemos usar assert para una invarianta interna:
        // si llegamos aquí, edad está en [0, 120]
        assert edad >= 0 && edad <= 120 : "Llegamos aquí con edad=" + edad;

        if (edad < 18) {
            IO.println("Menor de edad.");
        } else if (edad < 65) {
            IO.println("Adulto.");
        } else {
            IO.println("Jubilado.");
        }
    }
}

Puntos clave:

  1. if para validar entrada del usuario: siempre se ejecuta, con o sin -ea.
  2. assert para verificar una invarianta interna: si llegamos a ese punto, la edad debe estar en rango. Si no lo está, hay un bug.
  3. La aserción no sustituye al if: lo complementa.

11.8.4 Ejemplo 4: invariantes de bucle en una búsqueda

public class InvarianteBusqueda {

    public static void main(String[] args) {

        int[] numeros = {5, 8, 3, 9, 2, 7};
        int buscar = 9;
        int posicion = -1;

        for (int i = 0; i < numeros.length; i++) {
            // Invariante: si posicion != -1, entonces numeros[posicion] == buscar
            if (posicion != -1) {
                assert numeros[posicion] == buscar
                    : "posicion=" + posicion + " pero numeros[" + posicion + "]=" + numeros[posicion];
            }

            if (numeros[i] == buscar) {
                posicion = i;
            }
        }

        IO.println("Encontrado en la posición: " + posicion);
    }
}

Puntos clave:

  1. Invariante: si posicion != -1 (ya encontramos el elemento), entonces numeros[posicion] debe ser igual a buscar.
  2. Si el bucle asigna posicion a un índice incorrecto, la aserción falla.
  3. Solo se verifica cuando posicion != -1 (si no hemos encontrado nada, no hay nada que verificar).

11.8.5 Ejemplo 5: combinación de precondición, invariante y postcondición

public class AsertionCompleta {

    public static void main(String[] args) {

        int[] numeros = {3, 1, 4, 1, 5, 9, 2, 6};

        // === Precondición ===
        assert numeros != null : "El array no debería ser null";
        assert numeros.length > 0 : "El array no debería estar vacío";

        // Calcular la suma
        int suma = 0;
        for (int i = 0; i < numeros.length; i++) {
            // === Invariante de bucle ===
            assert i >= 0 && i < numeros.length : "i fuera de rango: " + i;

            suma = suma + numeros[i];
        }

        // === Postcondición: la suma debe ser positiva (todos los elementos son positivos) ===
        assert suma > 0 : "La suma debería ser positiva, pero es " + suma;

        IO.println("Suma: " + suma);
    }
}

Puntos clave:

  1. Precondición: array no null y no vacío, al inicio.
  2. Invariante de bucle: i está en rango [0, length-1], al inicio de cada iteración.
  3. Postcondición: la suma es positiva (porque todos los elementos lo son), al final.
  4. Tres tipos de aserción en un mismo programa: el patrón completo.

11.9 Buenas prácticas con assert

11.9.1 Las 8 reglas de las aserciones

  1. assert para bugs del programador, if/try-catch para errores del usuario.
  2. Activa -ea en desarrollo y desactívalo en producción.
  3. La condición debe ser pura: no modificar nada, solo comprobar.
  4. Siempre pon mensaje: assert cond : "mensaje con valores" es mucho más útil que assert cond.
  5. Nunca captures AssertionError: déjalo explotar.
  6. Precondiciones al inicio, postcondiciones al final.
  7. No uses assert para validación de entrada: el usuario no sabe si -ea está activado.
  8. No abuses: un assert en cada línea es ruido. Ponlos en puntos estratégicos donde un fallo sería difícil de detectar sin ellos.

11.9.2 Cuándo usar assert vs. if vs. try-catch

Situación Herramienta Razón
El usuario escribe "abc" donde esperabas un número. try-catch Error del usuario; hay que manejarlo.
La edad del usuario es 200. if Validación de entrada; hay que mostrar mensaje.
Una variable interna debería estar en rango. assert Si no lo está, es un bug interno.
Después de ordenar, el array no está ordenado. assert El algoritmo de ordenación tiene un bug.
Un fichero no existe. try-catch con IOException Error del entorno; hay que manejarlo.
Una referencia debería ser no-null en este punto. assert Si es null, hay un bug de lógica.

11.9.3 Comentar las aserciones

1
2
3
4
5
6
// MAL: la aserción no se explica
assert suma == 15;

// BIEN: la aserción explica qué verifica y por qué
// Postcondición: la suma de {1,2,3,4,5} es siempre 15
assert suma == 15 : "La suma debería ser 15, pero es " + suma;

11.10 Resumen y mapa conceptual

11.10.1 Mapa conceptual

Use mouse to pan and zoom
mindmap
  root((Sección 11 - assert))
    Sintaxis
      assert condicion
      assert condicion mensaje
      Si true continua
      Si false AssertionError
    Activación
      -ea en VM options
      Desactivado por defecto
      -da para desactivar
      Verificar con assert false
    assert vs excepciones
      assert para bugs del programador
      excepciones para errores del usuario
      assert no se ve en producción
      excepciones sí se ven
    Precondiciones
      Al inicio del bloque
      Verifican entradas
      Ejemplo array no null
    Postcondiciones
      Al final del bloque
      Verifican salidas
      Ejemplo suma correcta
    Invariantes de bucle
      Al inicio de cada iteración
      Verifican estado del bucle
      Costosas en bucles largos
    Trampas
      Olvidar -ea
      assert para entrada del usuario
      Efectos secundarios
      Capturar AssertionError
      assert como if

11.10.2 Las 10 ideas que no debes olvidar

  1. assert condicion; no hace nada si la condición es true; lanza AssertionError si es false.
  2. assert está desactivado por defecto: hay que poner -ea en VM options.
  3. assert condicion : "mensaje"; incluye un mensaje que ayuda a diagnosticar.
  4. assert es para bugs del programador, no para errores del usuario.
  5. if y try-catch son para errores del usuario y del entorno.
  6. Nunca captures AssertionError: si falla, hay un bug que arreglar.
  7. La condición debe ser pura: no modificar nada, solo comprobar.
  8. Precondiciones al inicio del bloque (verifican entradas); postcondiciones al final (verifican salidas).
  9. No uses assert para validar entrada del usuario: se ignora sin -ea.
  10. assert complementa al depurador: el depurador investiga, assert detecta.

11.10.3 Glosario rápido

Término Significado
Aserción (assertion) Afirmación que el programador considera siempre cierta en un punto del programa.
assert Palabra reservada de Java para escribir aserciones.
AssertionError Error que lanza Java cuando una aserción falla. Hereda de Error, no de Exception.
-ea Flag de la JVM que activa las aserciones (enable assertions).
-da Flag de la JVM que desactiva las aserciones (disable assertions). Por defecto.
Precondición Condición que debe ser cierta antes de ejecutar un bloque.
Postcondición Condición que debe ser cierta después de ejecutar un bloque.
Invariante de bucle Condición que debe ser cierta al inicio de cada iteración de un bucle.
Condición pura Expresión que no modifica nada, solo evalúa.
VM options Configuración de la JVM en IntelliJ: donde se pone -ea.
Bug Error en el código que hace que el programa se comporte de forma incorrecta.
Contrato interno Acuerdo que el programador hace consigo mismo: "en este punto, esto debe ser cierto".

11.11 Actividades propuestas

Cinco actividades graduadas. Hazlas en orden. Las soluciones se publicarán en Aules.

11.11.1 Actividad 1 — Verificar que -ea está activado

Objetivo: configurar IntelliJ para que las aserciones funcionen.

Enunciado: Crea el siguiente programa y ejecútalo:

1
2
3
4
5
6
7
public class PruebaAssert {

    public static void main(String[] args) {
        assert false : "Si ves esto, las aserciones están activadas.";
        IO.println("Si ves esto, las aserciones NO están activadas.");
    }
}

Tareas:

  1. Ejecuta con Shift+F10 sin activar -ea. Anota lo que ves.
  2. Ve a Run → Edit Configurations → VM options y escribe -ea.
  3. Ejecuta de nuevo. Anota lo que ves.
  4. Quita -ea y ejecuta de nuevo. Anota lo que ves.

Entrega: archivo PruebaAssert.java en el paquete jams.programacion.ud3 y un comentario al final con las tres observaciones.

Criterios evaluados: CE 3.6, CE 3.9.

11.11.2 Actividad 2 — Precondición y postcondición en un array

Objetivo: escribir aserciones de precondición y postcondición.

Enunciado: Escribe un programa que calcule el máximo de un array de enteros. Incluye:

  1. Precondición: el array no es null y no está vacío (dos assert).
  2. Postcondición: después del cálculo, el máximo es mayor o igual que cada elemento del array (un assert dentro de un bucle de verificación).

Requisitos:

  • Usa assert con mensaje en todos los casos.
  • Activa -ea en VM options para probar.
  • El array puede ser cualquiera: pruébalo con {5, 8, 3, 9, 2} y con {-5, -8, -3}.

Entrega: archivo MaximoConAssert.java en el paquete jams.programacion.ud3.

Criterios evaluados: CE 3.6, CE 3.9.

11.11.3 Actividad 3 — assert vs. if para validar entrada

Objetivo: entender la diferencia entre assert y if en validación de entrada.

Enunciado: Escribe un programa que pida la edad al usuario. Incluye:

  1. if para validar que la edad esté entre 0 y 120 (validación de entrada del usuario).
  2. assert después del if para verificar que, si llegamos a ese punto, la edad está en rango (contrato interno).

Tareas:

  1. Ejecuta con -ea activado y escribe edades válidas e inválidas. Anota el comportamiento.
  2. Ejecuta sin -ea y escribe una edad inválida (200). Anota qué pasa: ¿el assert se ignora? ¿el if sigue funcionando?
  3. Explica en un comentario al final por qué if es la herramienta correcta para validar entrada y assert es la herramienta correcta para verificar invariantes internas.

Entrega: archivo AssertVsIf.java en el paquete jams.programacion.ud3.

Criterios evaluados: CE 3.6, CE 3.9.

11.11.4 Actividad 4 — Invariante de bucle en una búsqueda

Objetivo: escribir un invariante de bucle para verificar un algoritmo de búsqueda.

Enunciado: Escribe un programa que busque un número en un array y devuelva su posición. Incluye un invariante de bucle: en cada iteración, si posicion != -1, entonces numeros[posicion] == buscar.

Requisitos:

  • Usa un array de al menos 8 elementos.
  • Pide al usuario el número a buscar (asume entrada válida).
  • El invariante va al inicio del cuerpo del bucle.
  • Usa assert con mensaje que incluya los valores de posicion, numeros[posicion] y buscar.
  • Activa -ea para probar.

Entrega: archivo BusquedaConInvariante.java en el paquete jams.programacion.ud3.

Criterios evaluados: CE 3.6, CE 3.9.

11.11.5 Actividad 5 — Cuestionario de repaso

Responde por escrito:

  1. ¿Qué es una aserción y para qué sirve?
  2. Escribe la sintaxis de assert con y sin mensaje.
  3. ¿Por qué assert está desactivado por defecto en Java? ¿Cómo se activa?
  4. ¿Qué es -ea y dónde se pone en IntelliJ?
  5. ¿Qué lanza Java cuando una aserción falla? ¿Es una Exception o un Error?
  6. ¿Cuál es la diferencia entre assert y throw new Excepcion? Pon un ejemplo de cada uno.
  7. ¿Por qué no debes usar assert para validar entrada del usuario?
  8. ¿Qué es una precondición? ¿Y una postcondición? Pon un ejemplo de cada una.
  9. ¿Qué es un invariante de bucle?
  10. ¿Por qué la condición de assert debe ser pura (sin efectos secundarios)?
  11. ¿Por qué no debes capturar AssertionError con catch?
  12. ¿Cuándo usarías assert vs. if vs. try-catch? Pon un ejemplo de cada caso.

Entrega: archivo cuestionario_seccion11.md con tus respuestas.

Criterios evaluados: CE 3.6, CE 3.9.


11.12 Lo que NO hemos visto (y dónde verlo)

Esta sección te ha enseñado assert como herramienta nativa de Java para aserciones. Hay cosas relacionadas que se reservan para unidades/secciones posteriores:

Concepto ¿Por qué no aquí? ¿Dónde se ve?
Testing con JUnit Framework de testing con aserciones profesionales (assertEquals, assertTrue...). UD2 (intro), UD4 (profundización).
Design by Contract (JML, etc.) Librerías externas para contratos formales. Fuera del módulo.
Logging en vez de assert para registro Frameworks de logging (SLF4J, Logback). UD5 / profesional.
Aserciones en métodos (precondición al inicio, postcondición antes de return) Los métodos se ven en UD4. UD4.
assert con lambdas y Streams Lambdas y Streams se ven en UD6. UD6.
Test de repaso de la sección Es la siguiente sección. Sección 12 de UD3.

11.13 Lo que viene en la sección 12

Con esta sección has completado el bloque de detección y corrección de errores de UD3: tienes el depurador (sección 10) para investigar bugs y assert (esta sección) para detectarlos automáticamente.

En la sección 12 verás el test de repaso, un cuestionario que cubre todas las secciones de UD3 (de la 1 a la 11) para que afiances los conceptos antes de los ejercicios integradores.

Después, en la sección 13 verás los ejercicios y actividades integradoras que cierran la unidad: un mini-juego "adivina el número", una calculadora robusta con menú y excepciones, operaciones sobre arrays y recorrido de matrices. Y la prueba objetiva RA3 (120 min, sin material).


Cierre del profesor. Has aprendido dos herramientas complementarias para la calidad del código: el depurador para investigar y assert para detectar. Juntas, forman el bloque de detección y corrección de errores de UD3. La regla más importante que te llevas es: assert para bugs del programador, if/try-catch para errores del usuario. Si interiorizas esa distinción, vas a escribir código más robusto y más fácil de depurar. Y recuerda: activa -ea siempre que vayas a probar aserciones, porque si no, es como ponerle una alarma a tu casa y no enchufarla. Nos vemos en la sección 12, donde haremos un repaso de toda la UD3 antes de los ejercicios integradores.