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

82

Así que para los operadores binarios en booleanos, Java tiene &, |, ^, &&y ||.

Resumamos brevemente lo que hacen aquí:

Porque &, el valor del resultado es truesi ambos valores de operando son true; de lo contrario, el resultado es false.

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

Porque ^, el valor del resultado es truesi los valores del operando son diferentes; de lo contrario, el resultado es false.

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

El ||operador es como |, pero evalúa su operando de la derecha solo si el valor de su operando de la izquierda es false.

Ahora, entre los 5, 3 de ellos tienen versiones de asignación compuesta, a saber |=, &=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" sea una buena respuesta, porque Java lo ha hecho >>>=. Debe haber una mejor razón para esta omisión.


De 15.26 Operadores de asignación :

Hay 12 operadores de asignación; [...]= *= /= %= += -= <<= >>= >>>= &= ^= |=


Se hizo un comentario de que si &&=y ||=se llevaron a cabo, entonces sería los únicos operadores que no evalúan el lado derecho primero. Creo que esta noción de que un operador de asignación compuesta evalúa primero el lado derecho es un error.

Desde 15.26.2 Operadores de asignación compuesta :

Una expresión de asignación compuesta de la forma E1 op= E2es equivalente a E1 = (T)((E1) op (E2)), donde Tes el tipo de E1, excepto que E1se evalúa solo una vez.

Como prueba, el siguiente fragmento arroja un NullPointerException, no un ArrayIndexOutOfBoundsException.

    int[] a = null;
    int[] b = {};
    a[0] += b[-1];
poligenelubricantes
fuente
2
Voy por el segundo, a nadie le importa: P también, todas estas preguntas sobre '¿por qué la característica x no está en el idioma y?' se debe preguntar a los diseñadores del lenguaje, no a nosotros: P
Federico klez Culloca
1
¿Qué significa & =? ¿Alguien por favor me puede decir?
Tarik
@ Aaron: a = a & b. Está escrito en la pregunta
Federico klez Culloca
1
posible duplicado de ¿Por qué no existe un operador "&& ="?
Josh Lee
1
@jleedev: Esa pregunta es más antigua, pero tiene más votos y enlaces entrantes. Yo diría que si hay alguna fusión, fusione la antigua con esta (sí, se puede hacer).
polygenelubricants

Respuestas:

27

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ó el &&=operador, 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 porque muchos desarrolladores pensarían que f2()siempre se llama cualquiera que sea el valor devuelto por f1 (). Es como bool isOk = f1() && f2();donde f2()se llama solo cuando f1()vuelve true.

Si el desarrollador quiere f2()ser llamado solo cuando f1()regresa true, por lo tanto, el segundo código anterior es menos propenso a errores.

De lo contrario, &=es suficiente porque el desarrollador quiere f2()que siempre se le llame:

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

Comparar &&y &resultados

Son los resultados de los operadores &&y &la misma cuando se aplica sobre valores booleanos?

Comprobemos usando el siguiente código 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 &valores booleanos ;-)

Así que es mejor usarlo en &=lugar de &&=.

Igual por ||=

Las mismas razones que para &&=: el
operador |=es menos propenso a errores que ||=.

Si un desarrollador desea que f2()no lo llamen cuando f1()regrese true, le 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 ...
oHo
fuente
1
Hola @StriplingWarrior. He consultado con mi colega Yannick, nuestro mejor experto en Java. He actualizado mi respuesta utilizando el código fuente de Java utilizado para verificar ese punto. Como dijiste &y &&da los mismos resultados. Muchas gracias por su colaboración. Te gusta mi respuesta Salud.
oHo
2
¿Qué pasa si quiero hacer esto muy rápido? && = sería más rápido que & =, si existiera, por lo que debería usarlo if (a) a = bpara la velocidad
adventurerOK
Hola @adventurerOK. Lo siento, no estoy seguro de entender lo que quieres decir ... Creo que a&=b;es más rápido que if(a) a=b;cuando se usan valores almacenados en los registros de la CPU. Sin embargo, si bestá en la memoria externa (no en caché), if(a) a=b;es más rápido. ¿Es a lo que te refieres? Proporcione más código de ejemplo ;-) Tengo curiosidad por conocer su opinión. Nos vemos. Saludos
oHo
12
No estoy de acuerdo cuando dices "Y esto no es lo que queremos". Si escribo, isOK &&= f2();me gustaría que hiciera un cortocircuito como lo &&hace.
Tor Klingberg
3
No estoy de acuerdo con su afirmación de que los operadores serían propensos a errores o inútiles. El uso de asignaciones compuestas es algo que se hace para tomar un atajo para lo habitual A = A op B, de modo que todos sepan perfectamente lo que están haciendo y puedan ocuparse de las implicaciones. Si sus razones fueran realmente la causa de su ausencia, lo vería como una condescendencia no deseada. Sin embargo, quiero agradecerles por agregar la línea bool isOk = f1() || f2() || f3() || f4();porque eso es lo que estaba ciego para verme.
Franz B.
16

Probablemente porque algo como

x = false;
x &&= someComplexExpression();

parece que debería asignar xy evaluar someComplexExpression(), pero el hecho de que la evaluación depende del valor dex no es evidente en la sintaxis.

También porque la sintaxis de Java se basa en C, y nadie vio la necesidad urgente de agregar esos operadores. De todos modos, probablemente estaría mejor con una declaración if.

Josh Lee
fuente
37
Creo que esta no es una buena respuesta, porque se puede argumentar que x() && y() parece que debería evaluar ambos lados de la expresión. Obviamente, la gente acepta que &&es un cortocircuito, por lo que también debería seguir &&=.
polygenelubricants
2
@jleedev, estuvo de acuerdo. Creo que en estas situaciones es importante recordar que esto no es el equivalente de x = x && someComplexExpression () sino el equivalente de x = someComplexExpression () && x. El lado derecho será / debería ser evaluado primero para ser consistente con todos los demás operadores de asignación. Y dado eso, && = no tendría un comportamiento diferente al de & =.
PSpeed
@polygene Eso es justo. Sin embargo, la apariencia de algo se basa en lo que conoces, y supongo que no querían agregar nada nuevo. Una cosa que me gusta de C / C ++ / Java / C # es que la sintaxis básica de las expresiones es casi idéntica.
Josh Lee
1
@PSpeed, estás equivocado. El JLS es muy claro sobre lo que se supone que debe hacer la asignación compuesta. Vea mi adición arriba.
polygenelubricants
1
@PSpeed: En ese caso, a -= b;funcionaría de manera completamente diferente a a &&= b;.
David Thornley
10

Es así 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 de B), la variedad & = de operadores simplemente se pasó por alto.

Pero la segunda parte de mi respuesta no tiene ninguna fuente que la respalde.

EFraim
fuente
1
En el lado divertido: la pregunta se hizo recientemente en el foro de C; y esta respuesta estaba vinculada (aunque no marcada como duplicada) ¡Lo que completa el argumento circular!
UKMonkey
5

En gran parte porque la sintaxis de Java se basa en C (o al menos la familia C), y en C todos esos operadores de asignación se compilan en instrucciones de ensamblaje aritméticas o bit a bit en un solo 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 optimizados. Los operadores lógicos (como se denominan en C) equivalentes ( &&=y ||=) no tienen una correspondencia tan obvia con instrucciones de ensamblaje individuales; generalmente se expanden a una secuencia de instrucciones de prueba y ramificación.

Curiosamente, idiomas como el rubí no tienen || = y = &&.

Editar: la terminología difiere entre Java y C

p00ya
fuente
Creo que quiere decir que los equivalentes de operadores condicionales no tienen una correspondencia tan obvia. En terminología JLS, los operadores lógicos booleanos son &, |y ^; &&y ||son los operadores condicionales.
Polygenelubricants
En terminología C, && y || son "operadores lógicos", s6.5.13-14 en ISO 9899: 1999. Los operadores bit a bit son sólo "lógicos" cuando se aplican a un solo bit (un booleano en Java); no hay un tipo de un solo bit en C y los operadores lógicos se aplican a todos los tipos escalares.
p00ya
antes de C99, C ni siquiera tenía un booltipo. Son 20 años en la historia del lenguaje sin bool. No había ninguna razón para hacerlo &&=. las operaciones bit a bit fueron suficientes.
v.oddou
4

Uno de los objetivos originales de Java era ser "simple, orientado a objetos y familiar". Según se aplica a este caso, & = es familiar (C, C ++ lo tiene y familiar en este contexto significa familiar para alguien que conoce a esos dos).

&& = no sería familiar, y no sería simple, en el sentido de que los diseñadores del lenguaje no buscaban pensar en todos los operadores que podrían agregar al lenguaje, por lo que menos operadores adicionales son más simples.

Yishai
fuente
3

Para variables booleanas, && y || usaría la evaluación de cortocircuito mientras & y | no lo haga, 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, desea ser rápido, eficiente y conciso.

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 sepa que una vez que bVal sea verdadero, no se llamará a fn () durante el resto de las iteraciones.

zanfilip
fuente
1
Para ese bucle probablemente solo quieras hacer if (fn(item)) { bVal = true; break; }.
Radiodef
2

' &' y ' &&' no son lo mismo que ' &&' es una operación de atajo que no funcionará si el primer operando es falso mientras '& ' lo hará de todos modos (funciona tanto con números como con booleanos).

Estoy de acuerdo en que tiene más sentido existir, pero no es tan malo si no está ahí. Supongo que no estaba ahí porque C no lo tiene.

Realmente no puedo pensar en por qué.

NawaMan
fuente
0

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 el carácter antes de =

zzawaideh
fuente
1
Java admite << =, >> = y >>> =, por lo que eso no es estrictamente cierto.
Yishai
cierto. No pensé en esos. Supongo que la única explicación es la frecuencia con la que se usa.
zzawaideh
0

No puedo pensar en una razón mejor que '¡Se ve increíblemente feo!'

Daniel Brückner
fuente
9
Pero entonces C / Java nunca tuvo la intención de ser hermoso.
EFraim
1
Apenas lo consideraría &&=feo
pregunta
0

Y

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 valores 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 && = no agregaría realmente una nueva funcionalidad al idioma. La aritmética del operador bit a bit es mucho más expresiva, puede hacer aritmética bit a bit enteros, que incluye aritmética booleana. Los operadores lógicos pueden simplemente hacer aritmética booleana.

Ely
fuente
0

Brian Goetz (arquitecto de lenguaje Java en Oracle) escribió :

https://stackoverflow.com/q/2324549/ [esta pregunta] muestra que hay interés en tener estos operadores y no hay argumentos claros por los que aún no existen. Por lo tanto, la pregunta es: ¿El equipo de JDK ha discutido la posibilidad de agregar estos operadores en el pasado y, de ser así, cuáles son las razones para no agregarlos?

No tengo conocimiento de ninguna discusión específica sobre este tema en particular, pero si alguien lo propusiera, la respuesta probablemente sería: no es una solicitud irrazonable, pero no tiene su peso.

"Llevar su peso" debe juzgarse por sus costos y beneficios, y por su relación costo-beneficio en relación con otras características candidatas.

Creo que está asumiendo implícitamente (con la frase "hay interés") que el costo es cercano a cero y el beneficio es mayor que cero, por lo que parece una ganancia obvia. Pero esto contradice una comprensión incorrecta del costo; una característica como esta afecta la especificación del lenguaje, la implementación, el JCK y todos los libros de texto de IDE y Java. No hay características de lenguaje triviales. Y el beneficio, aunque distinto de cero, es bastante pequeño.

En segundo lugar, hay infinitas funciones que podríamos hacer, pero solo tenemos la capacidad de hacer algunas cada pocos años (y los usuarios tienen una capacidad limitada para absorber nuevas funciones). Por lo tanto, debemos tener mucho cuidado con las que elegimos cada característica (incluso una que parezca trivial) consume parte de este presupuesto e invariablemente se lo quita de otras.
No se trata de "por qué no esta función", sino "¿qué otras funciones no haremos (o retrasaremos) para poder hacer esta? ¿Es una buena operación?" Y realmente no puedo imaginar que este sea un buen intercambio frente a cualquier otra cosa en la que estemos trabajando.

Por lo tanto, borra la barra de "no es una idea terrible" (lo cual ya es bastante bueno, muchas solicitudes de funciones ni siquiera aclaran eso), pero parece poco probable que supere la barra de "un mejor uso de nuestro presupuesto de evolución que cualquier otra cosa."

Marcono1234
fuente
-1

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

¿Qué crees que son iguales?

Omry Yadan
fuente
1
a & b, a diferencia de a && b, siempre debe evaluar b.
Daniel Brückner