relacionales - ¿Por qué Java no tiene versiones de asignación compuesta de los operadores condicional, y condicional o?(&&=, ||=)
precedencia de operadores java (11)
Razón
Los operadores &&=
y ||=
no están disponibles en Java porque para la mayoría de los desarrolladores estos operadores son:
- propenso a errores
- inútil
Ejemplo para &&=
Si Java permitió &&=
operator, entonces ese código:
bool isOk = true; //becomes false when at least a function returns false
isOK &&= f1();
isOK &&= f2(); //we may expect f2() is called whatever the f1() returned value
sería equivalente a:
bool isOk = true;
if (isOK) isOk = f1();
if (isOK) isOk = f2(); //f2() is called only when f1() returns true
Este primer código es propenso a errores debido a que muchos desarrolladores pensarían que f2()
siempre se llama sea cual sea el valor devuelto f1 (). Es como bool isOk = f1() && f2();
donde f2()
se invoca solo cuando f1()
devuelve true
.
Si el desarrollador desea que se llame a f2()
solo cuando f1()
devuelve true
, por lo tanto, el segundo código anterior es menos propenso a errores.
Else &=
es suficiente porque el desarrollador quiere que siempre se llame a f2()
:
El mismo ejemplo pero para &=
bool isOk = true;
isOK &= f1();
isOK &= f2(); //f2() always called whatever the f1() returned value
Además, la JVM debería ejecutar este código anterior como el siguiente:
bool isOk = true;
if (!f1()) isOk = false;
if (!f2()) isOk = false; //f2() always called
Compara &&
y &
resultados
¿Los resultados de los operadores &&
y &
los mismos cuando se aplican a valores booleanos?
Comprobemos usando el siguiente código de Java:
public class qalcdo {
public static void main (String[] args) {
test (true, true);
test (true, false);
test (false, false);
test (false, true);
}
private static void test (boolean a, boolean b) {
System.out.println (counter++ + ") a=" + a + " and b=" + b);
System.out.println ("a && b = " + (a && b));
System.out.println ("a & b = " + (a & b));
System.out.println ("======================");
}
private static int counter = 1;
}
Salida:
1) a=true and b=true
a && b = true
a & b = true
======================
2) a=true and b=false
a && b = false
a & b = false
======================
3) a=false and b=false
a && b = false
a & b = false
======================
4) a=false and b=true
a && b = false
a & b = false
======================
Por lo tanto, SÍ , podemos reemplazar &&
por &
para valores booleanos ;-)
Así que es mejor usar &=
lugar de &&=
.
Lo mismo para ||=
Las mismas razones que para &&=
:
operador |=
es menos propenso a errores que ||=
.
Si un desarrollador quiere que f2()
no se llame cuando f1()
devuelve true
, entonces te aconsejo las siguientes alternativas:
// here a comment is required to explain that
// f2() is not called when f1() returns false, and so on...
bool isOk = f1() || f2() || f3() || f4();
o:
// here the following comments are not required
// (the code is enough understandable)
bool isOk = false;
if (!isOK) isOk = f1();
if (!isOK) isOk = f2(); //f2() is not called when f1() returns false
if (!isOK) isOk = f3(); //f3() is not called when f1() or f2() return false
if (!isOK) isOk = f4(); //f4() is not called when ...
Entonces para los operadores binarios en booleanos, Java tiene &
, |
, ^
, &&
y ||
.
Vamos a resumir lo que hacen brevemente aquí:
- JLS 15.22.2 operadores lógicos booleanos &, ^, y |
- JLS 15.23 Condicional-y operador &&
- JLS 15.24 Condicional-O Operador ||
Para
&
, el valor del resultado estrue
si ambos valores del operando sontrue
; de lo contrario, el resultado esfalse
.Para
|
, el valor del resultado esfalse
si ambos valores del operando sonfalse
; de lo contrario, el resultado estrue
.Para
^
, el valor del resultado estrue
si los valores del operando son diferentes; de lo contrario, el resultado esfalse
.El operador
&&
es como&
pero evalúa su operando de la mano derecha solo si el valor de su operando de la mano izquierda estrue
.El
||
el operador es como|
, pero evalúa su operando de la mano derecha solo si el valor de su operando de la mano izquierda esfalse
.
Ahora, entre los 5, 3 de ellos tienen versiones de asignación compuesta, es decir |=
, &=
y ^=
. Entonces mi pregunta es obvia: ¿por qué Java no proporciona &&=
y ||=
también? Encuentro que los necesito más de lo que necesito &=
y |=
.
Y no creo que "porque es demasiado largo" es una buena respuesta, porque Java tiene >>>=
. Debe haber una mejor razón para esta omisión.
Desde 15.26 Operadores de Asignación :
Hay 12 operadores de asignación; [...]
= *= /= %= += -= <<= >>= >>>= &= ^= |=
Se hizo un comentario que si se implementaron &&=
y ||=
, entonces serían los únicos operadores que no evalúan primero el lado derecho. Creo que esta noción de que un operador de asignación compuesta evalúe primero el lado derecho es un error.
Desde 15.26.2 Operadores de Asignación de Compuestos :
Una expresión de asignación compuesta de la forma
E1 op= E2
es equivalente aE1 = (T)((E1) op (E2))
, dondeT
es el tipo deE1
, excepto queE1
se evalúa solo una vez.
Como prueba, el siguiente fragmento arroja una NullPointerException
, no una ArrayIndexOutOfBoundsException
.
int[] a = null;
int[] b = {};
a[0] += b[-1];
&
verifica ambos operandos, es un operador bit a bit. Java define varios operadores bit a bit, que se pueden aplicar a los tipos enteros, long, int, short, char y byte.
&&
deja de evaluar si el primer operando se evalúa como falso ya que el resultado será falso, es un operador lógico. Se puede aplicar a booleanos.
El operador && es similar al operador &, pero puede hacer que su código sea un poco más eficiente. Debido a que ambas expresiones comparadas por el operador & deben ser verdaderas para que toda la expresión sea verdadera, no hay razón para evaluar la segunda expresión si la primera devuelve falso. El operador & siempre evalúa ambas expresiones. El operador && evalúa la segunda expresión solo si la primera expresión es verdadera.
Tener un operador de asignación && = realmente no agregaría nuevas funcionalidades al lenguaje. La aritmética del operador bit a bit es mucho más expresiva, puede hacer aritmética bit a bit entera, que incluye aritmética booleana. Los operadores lógicos pueden simplemente hacer aritmética booleana.
'' &
'' y '' &&
'' no son lo mismo que '' &&
'' es una operación de acceso directo que no funcionará si el primer operando es falso, mientras que '' &
'' lo hará de todos modos (funciona con números y booleanos).
Estoy de acuerdo en que tiene más sentido existir, pero no es tan malo si no está allí. Supongo que no estaba allí porque C no lo tiene.
Realmente no puedo pensar por qué.
Es de esta manera en Java, porque es así en C.
Ahora la pregunta de por qué es así en C es porque cuando & y && se convirtieron en operadores diferentes (en algún momento antes del descenso de C desde B), la variedad de operadores & = simplemente se pasó por alto.
Pero la segunda parte de mi respuesta no tiene ninguna fuente para respaldarlo.
Está permitido en Ruby.
Si tuviera que adivinar, diría que no se usa con frecuencia, por lo que no se implementó. Otra explicación podría ser que el analizador solo mira al personaje antes de =
No puedo pensar en una mejor razón entonces ''¡parece increíblemente feo!''
Para vars booleanos, && y || usaría la evaluación de cortocircuito mientras & y | no, por lo que esperaría que && = y || = también usen la evaluación de cortocircuito. Hay un buen caso de uso para esto. Especialmente si está iterando sobre un bucle, quiere ser rápido, eficiente y concisa.
En lugar de escribir
foreach(item in coll)
{
bVal = bVal || fn(item); // not so elegant
}
Quiero escribir
foreach(item in coll)
{
bVal ||= fn(item); // elegant
}
y saber que una vez que bVal sea verdadero, no se llamará a fn () para el resto de las iteraciones.
Principalmente porque la sintaxis de Java se basa en C (o al menos en la familia C), y en C todos los operadores de asignación se compilan en instrucciones de ensamblaje aritmético o en bits en un único registro. La versión del operador de asignación evita los temporales y puede haber producido un código más eficiente en los primeros compiladores no optimizadores. Los operadores lógicos (como se denominan en C) equivalentes ( &&=
y ||=
) no tienen una correspondencia tan obvia con las instrucciones de ensamblaje único; generalmente se expanden a una secuencia de instrucciones de prueba y ramificación.
Curiosamente, los lenguajes como ruby tienen || = y && =.
Editar: la terminología es diferente entre Java y C
Probablemente porque algo como
x = false;
x &&= someComplexExpression();
parece que debería asignarse a x
y evaluar someComplexExpression()
, pero el hecho de que la evaluación depende del valor de x
no es evidente a partir de la sintaxis.
También porque la sintaxis de Java se basa en C, y nadie vio una necesidad apremiante de agregar esos operadores. Probablemente estarías mejor con una declaración if, de todos modos.
Uno de los aims originales de Java era ser "Simple, orientado a objetos y familiar". Como se aplica a este caso, & = es familiar (C, C ++ lo tiene y familiar en este contexto significa familiar para alguien que conoce esos dos).
&& = no sería familiar, y no sería simple, en el sentido de que los diseñadores de lenguaje no pensaban en cada operador que podrían agregar al lenguaje, por lo que menos operadores adicionales son más simples.
a & b y a && b no son lo mismo.
a && b es una expresión booleana que devuelve un booleano, y a & b es una expresión bit a bit que devuelve un int (si a y b son ints).
¿Por qué crees que son lo mismo?