¿Por qué Java no tiene versiones de asignación compuesta de los operadores condicional, y condicional o? (&& =, || =)

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 es true si ambos valores del operando son true ; de lo contrario, el resultado es false .

Para | , el valor del resultado es false si ambos valores del operando son false ; de lo contrario, el resultado es true .

Para ^ , el valor del resultado es true si los valores del operando son diferentes; de lo contrario, el resultado es false .

El operador && es como & pero evalúa su operando de la mano derecha solo si el valor de su operando de la mano izquierda es true .

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 es false .

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 a E1 = (T)((E1) op (E2)) , donde T es el tipo de E1 , excepto que E1 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]; 

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, , 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 ... 

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 syntax.

También porque la syntax de Java se basa en C, y nadie vio una necesidad apremiante de agregar esos operadores. Probablemente estarías mejor con una statement if, de todos modos.

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.

Uno de los objectives 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.

Principalmente porque la syntax de Java se basa en C (o al menos en la familia C), y en C todos los operadores de asignación se comstackn 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 comstackdores 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

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 rest de las iteraciones.

& ‘ 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é.

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 =

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?

No puedo pensar en una mejor razón entonces ‘¡parece increíblemente feo!’

&

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.

Intereting Posts