Estoy preparando varios artículos sobre estructuras básicas en Java, como if/else, (do) while, for y for-each. Sin embargo, antes de empezar quiero aclarar cómo funciona la condición booleana que se evalúa en dichas estructuras. El tipo de datos booleano representa una verdad simple: verdadero o falso, sí o no, activado o desactivado. En Java, booleano es un tipo de datos primitivo que sólo puede tomar uno de estos dos valores.
La palabra "booleano" en el contexto del lenguaje Java se puede usar en significados diferentes, aunque muy relacionados. En este artículo, cubriremos todas estas opciones y explicaremos qué conceptos subyacen a las expresiones booleanas.
¿Qué es un booleano en sentido general?
El concepto de expresión booleana proviene de las matemáticas, o mejor dicho, de la lógica matemática. Una expresión booleana en álgebra proposicional es una expresión de la que se puede decir que es verdadera o falsa. Al mismo tiempo, "2" o "nieve" no son expresiones booleanas.
Los tipos de datos primitivos son los componentes básicos de datos que puedes manipular en tu código. En este contexto, profundicemos en un tipo de datos primitivo específico: booleano.
Tipo de Datos Primitivo Booleano de Java y Variables Booleanas
Hablando de un booleano en Java, primero es muy probable que sea un tipo de datos primitivo booleano y variables booleanas de este tipo. Como probablemente ya hayas adivinado, las variables de este tipo solo pueden tomar dos valores, verdadero y falso.
Lea también: IVA 21% Excel
Java tiene restricciones bastante estrictas: un booleano en Java no se puede convertir a ningún otro tipo de datos y viceversa. En particular, boolean en Java no es un tipo integral y no se pueden usar valores enteros en lugar de booleanos. A diferencia de otros lenguajes de programación, en Java no puedes asignar a una variable booleana un número entero (como 0 o 1).
Las variables booleanas se utilizan a menudo para controlar el flujo de un programa. Por ejemplo, es posible que desee ejecutar un bloque de código sólo si una condición específica es verdadera. Este programa imprime "falso" en la consola. Por cierto, una variable booleana se establece en falso de forma predeterminada, pero Java no le permite trabajar con variables locales no inicializadas.
Expresiones Booleanas en Java y su Uso
Además de inicializar explícitamente una variable booleana en verdadero o falso, el tipo de datos booleano se usa implícitamente en muchos lugares. Así como el resultado de cualquier suma de números será un número, el resultado de cualquier comparación será verdadero o falso, es decir, será de tipo booleano. Esto significa que, además de especificar directamente un valor booleano a través de la declaración de asignación de variables booleanas, los valores booleanos resultan de varias comparaciones, como 5 > 2, y se usan principalmente en declaraciones condicionales y de bucle.
Las estructuras básicas en Java son aquellos elementos sintácticos sencillos que aparecen continuamente en cualquier programa en Java. Como todas estas estructuras evalúan una condición booleana, sobre la que basan su comportamiento, vamos a empezar primero por ver en qué consiste esta condición, y qué forma puede tomar.
La condición que se evalúa en estas estructuras básicas en Java puede presentarse de distintas formas, pero su valor ha de ser siempre true (verdadero) o false (falso). A continuación te pongo varios ejemplos de lo que te puedes encontrar:
Lea también: Guía IVA reducido
- En primer lugar, puedes encontrarte un valor booleano, ya sea como variable, objeto o directamente: true/false.
- En segundo lugar, es posible que te encuentres con una comparación entre dos objetos o variables.
- Por último, puedes encontrarte con una cadena de condiciones.
A continuación vamos a ver con más detenimiento cada uno de estos casos, con ejemplos prácticos. También examinaremos las posibles trampas.
Primer caso: la condición es un valor booleano
Un valor booleano es aquel que solo admite true (verdadero) o false (falso).
En Java, aparte de las variables primitivas, también existen clases envoltorio (wrappers), que vienen a ser objetos que equivalen a esas mismas variables primitivas. Así, la clase envoltorio de una variable boolean es un objeto de tipo Boolean. Para instanciar un objeto de tipo Boolean, puedes asignarle un valor booleano, o utilizar uno de los constructores.
La clase envoltorio Boolean posee dos constructores, de manera que puedes crear un objeto de tipo Boolean. Por ejemplo, te he dicho antes que no se puede asignar un número entero a una variable booleana, y tampoco puedes utilizarlo para instanciar un objeto de tipo Boolean. Si te fijas bien, aquí tampoco lo he hecho: el número está entre comillas, lo que quiere decir que he instanciado el objeto con una String.
Cuidado con el constructor new Boolean(String)
Cuando utilices el constructor new Boolean(String), el objeto que instancias tomará el valor true si utilizas la String "true" (en mayúscula o minúscula). Cualquier otra String hará que el objeto tome el valor false.
Lea también: ¿Cómo localizar tus XML del SAT?
Por último, ten en cuenta que una asignación toma el valor de la asignación. Es decir, si ya has declarado la variable booleana first, puedes utilizar como condición la asignación first = false. Esta condición tiene el valor false, el mismo que se asigna.
Como conclusión de esta primera parte, la condición que se utiliza en una estructura básica en Java puede ser:
- Una variable booleana.
- Un objeto de tipo Boolean.
- Una llamada a uno de los constructores de la clase Boolean (presta atención al constructor que acepta una String).
- La asignación de un valor booleano.
Segundo caso: la condición es una comparación entre dos objetos o variables
Debes tener muy claro qué es la igualdad cuando se comparan dos objetos en Java. Sobre todo, cuando comparas dos objetos de tipo String. Como sabes, puedes comparar dos objetos de tipo String utilizando == (o con !=, para indicar que son distintos) o con el método equals(String).
Diferencia entre == y equals(String)
La diferencia es que == te dice si ambos objetos comparados son el mismo. Mejor dicho: te dice si las dos referencias que estás comparando apuntan al mismo objeto. El método equals(String) te dice si ambas String tienen el mismo valor, no si las referencias apuntan al mismo objeto. Por ello, como utilizan criterios distintos para comparar, ambos pueden darte valores distintos, dado el caso:
| == | equals(String) | |
|---|---|---|
| Ambas referencias apuntan al mismo objeto | true | true |
| Ambas referencias apuntan a objetos distintos | false | true o false |
Si ambas referencias de tipo String apuntan al mismo objeto, al compararlas con == obtendrás true y, por supuesto, obtendrás el mismo resultado al compararlas con equals(String). En cambio, si las referencias apuntan a objetos distintos, al compararlas con == obtendrás false. Sin embargo, dos objetos distintos pueden poseer el mismo valor, así que al compararlos con equals(String) puedes obtener true o false, si los objetos poseen valores distintos.
Como has visto en el ejemplo, se pueden instanciar objetos de tipo String de varias maneras, pero al compararlos resultan ser el mismo objeto. Esto se debe a que los valores asignados a objetos de tipo String se reutilizan. La única manera de asignar un valor nuevo a un objeto de tipo String es utilizando el constructor. Incluso si utilizas un valor que ya existe, si lo asignas utilizando el constructor, ese objeto se considera "nuevo". El método intern() convierte ese objeto "nuevo" en un objeto reutilizado.
Todos los clases en Java disponen del método equals(), que heredan de la clase Object y que acepta un parámetro del mismo tipo que la clase que hereda el método. Sin embargo, este método equivale a ==. Algunas clases, como String, sobreescriben este método heredado, de manera que para la clase String este método funciona con un criterio diferente.
En el caso de que evalúes cifras, puedes utilizar < (menor que), > (mayor que), <= (menor o igual), >= (mayor o igual) y != (distinto de). Todas estas comparaciones también tienen como resultado true o false.
Tercer caso: la condición es, en realidad, una cadena de condiciones
¿Qué pasa si, por ejemplo, quieres que se evalúen varias condiciones? Podría ser que no necesitases que todas esas condiciones fuesen verdaderas; quizá solo la primera, o la segunda. En ese caso, puedes utilizar dos tipos de operadores: los operadores ansiosos & y | o los operadores de cortocircuito && y ||.
& y && equivalen a "y". El primer operador se utiliza para indicar que ambas condiciones han de ser verdaderas.
| y || equivalen a "o". El segundo, para indicar que una de las condiciones, como mínimo, ha de ser verdadera.
Diferencia entre & / | y && / ||
La diferencia que existe entre los operadores ansiosos (simples) y los de cortocircuito (dobles) es que los operadores ansiosos evalúan siempre todas las condiciones. Por el contrario, los operadores de cortocircuito dejan de evaluar condiciones si pueden deducir el resultado.
Por ejemplo, imagínate que quieres tomarte un café en el bar de al lado. En ese caso, has de evaluar dos condiciones, y ambas han de ser verdaderas: tengo dinero & tengo tiempo. Pero suponte que te tomas el café, pero a la hora de pagar te das cuenta de que has perdido la cartera. El camarero puede colocarte ante la siguiente tesitura: friegas tazas sucias | llamo a la policía. En este caso, te deja la elección. Basta con que uno de los dos sea true. No obstante, como está utilizando un operador ansioso, ya está con el teléfono en la mano.
Si la evaluación es con operadores de cortocircuito: tengo dinero && tengo tiempo. Si no tienes dinero ni te vas a plantear si tienes tiempo o no. Ambas condiciones tienen que ser verdaderas, así que si la primera es falsa, no es necesario evaluar la segunda. Si el camarero se lo toma de la siguiente manera: friegas tazas sucias || llamo a la policía. Ni se planteará llamar a la policía, porque ya has saltado sobre las tazas. Una de las condiciones ha de ser verdadera, y ha sido la primera. No es necesario evaluar la segunda.
Consecuencias de evaluar (o no) la segunda condición
Como ya te he dicho más arriba, las condiciones pueden ser estructuras complejas, cuyo resultado final sea verdadero o falso. Estas estructuras pueden contener asignaciones u otros operadores que modifiquen el valor asignado a variables ya declaradas. Fíjate en el siguiente ejemplo: Como ves, la segunda condición se evalúa con los operadores ansiosos (simples), aunque sea innecesario, ya que la primera condición es siempre verdadera.
Operadores Booleanos de Java
Echemos un vistazo a la tabla con una breve descripción de cada uno de los operadores booleanos de Java y, a continuación, los describiremos con más detalle y daremos ejemplos de código. Por "operandos" en la tabla, nos referimos a la expresión lógica o variables a las que se aplica el operador.
Negación lógica, también es NOT o inversión. En Java, denotado por el símbolo !. !x significa “no x”. Devuelve verdadero si x es falso.
Y lógico, también es AND o conjunción. Operadores & y &&.
Lógico o en Java, también es OR, también es disyunción. Operadores | y ||.
Disyunción exclusiva o, XOR, estricta. Operador ^. (a ^ b) devuelve verdadero, si solo uno de los operandos (a o b) es verdadero. Devuelve falso, si a y b son verdaderos o falsos simultáneamente.
Tenga en cuenta que en Java, los operadores & , | y ^ también se aplican a números enteros. En este caso, funcionan un poco diferente y se denominan operadores lógicos bit a bit (o bit a bit). De hecho, puedes hacer construcciones lógicas muy complejas utilizando operadores lógicos. Si se inicializan todas las variables, tales construcciones funcionarán. Sin embargo, no debes abusar de ellos, dificultan la lectura del código. Sin embargo, es muy útil tratar con tales construcciones lógicas.
Precedencia de operaciones lógicas
Al igual que en matemáticas, en programación, los operadores tienen un orden específico de ejecución si ocurren en la misma expresión. Los operadores unarios tienen ventajas sobre los binarios y la multiplicación (incluso la lógica) sobre la suma.
Clase Envoltorio Booleana de Java
En Java, cada tipo primitivo tiene un "hermano", una clase contenedora (Wrapper). Un contenedor es una clase especial que almacena el valor de una primitiva en su interior. Sin embargo, esta es una clase, por lo que puede crear instancias (objetos) de ella. Estos objetos almacenan en su interior los valores necesarios de las primitivas, mientras que serán objetos reales.
El tipo primitivo booleano de Java tiene una clase contenedora booleana de Java (con B mayúscula). La clase booleana de Java tiene métodos útiles. Uno de los más interesantes es el método parseBoolean. El método booleano estático parseBoolean(String s) analiza el argumento de cadena como un booleano. El booleano devuelto representa el valor verdadero si el argumento de cadena no es nulo y es igual, ignorando mayúsculas y minúsculas, a la cadena "verdadero". De lo contrario, devuelve falso.
Ejercicios Recomendados
En este primer artículo sobre estructuras básicas en Java, te recomiendo que ejercites sobre todo cómo instanciar objetos de tipo String. ¿Te atreves a intentar adivinar si las siguiente condiciones son verdaderas o falsas? Ejecuta el código e intenta explicar el resultado: ¡Cuéntame en los comentarios si has acertado!
