Java – Operador Ternario ?

El ternario es un if-else que devuelve un valor. Esa es toda la diferencia, y también la razón por la que a veces encaja y a veces no.

De if a ternario

int i = 1, j = 2;
int mayor;

if (i > j) {
    mayor = i;
} else {
    mayor = j;
}

Seis líneas para decidir un valor. Con el ternario:

int mayor = i > j ? i : j;
salidamayor: 2

La forma es siempre la misma: condición ? valor_si_es_cierto : valor_si_es_falso. Como el resultado es un valor, puede ir donde vaya un valor: en una asignación, en un argumento o dentro de una concatenación.

System.out.println(i > j ? "i es mayor" : "j es mayor");
salidaj es mayor

La diferencia que importa

Un if ejecuta sentencias; un ternario produce un valor. Por eso esto es válido y lo otro no:

int x = condición ? 1 : 2;          // bien: las dos ramas son valores
// int y = condición ? System.out.println("hola") : 2;   // no compila: println no devuelve nada

Y de ahí sale la regla para decidir: si lo que quieres es elegir un valor, ternario; si lo que quieres es hacer dos cosas distintas, if. Forzar un ternario para provocar efectos secundarios se lee mal y a veces ni compila.

La trampa: int e Integer en la misma expresión

public class Trampa {
    static Integer edad = null;      // viene de una base de datos, por ejemplo

    public static void main(String[] args) {
        Object c = true ? edad : 0;  // una rama Integer, la otra int
        System.out.println(c);
    }
}
salidaException in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "Trampa.edad" is null
	at Trampa.main(Trampa.java:5)
Fíjate en que el resultado se guarda en un Object. Parecería que ahí cabe un null sin problema, y sin embargo revienta. Lo que pasa es que el tipo del ternario se decide antes: al mezclar Integer con int, el lenguaje concluye que el resultado es int, y para eso tiene que desempaquetar el Integer llamando a intValue(). Sobre null, eso es un NullPointerException.

La solución es no mezclar: que las dos ramas sean del mismo tipo.

Object c = true ? edad : (Integer) 0;    // las dos Integer: no hay desempaquetado
int    d = (edad != null) ? edad : 0;    // o compruebas antes, que es lo normal
salidac = null

Y conviene saber que esto no falla, aunque lo parezca:

int a = true ? 1 : edad;    // la rama de edad no se evalúa
System.out.println("a = " + a);
salidaa = 1

Solo se desempaqueta la rama que se ejecuta. Que falle o no depende de cuál sea, y por eso el fallo aparece en producción y no en las pruebas.

Cuándo no usarlo

// ilegible
String r = a ? (b ? "uno" : "dos") : (c ? "tres" : "cuatro");

Un ternario dentro de otro se puede escribir, pero hay que leerlo tres veces. En cuanto hay más de una condición, un if o un switch con flecha se entienden a la primera. El ternario brilla en lo corto: un valor por omisión, un singular o plural, un signo.

String texto = n + " archivo" + (n == 1 ? "" : "s");

Más información en inglés: Ternary operator in Java.

Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.