¿Por qué el tipo de datos de su declaración de cambio no puede ser largo, Java?

80

Aquí hay un extracto de los tutoriales de Java de Sun :

Un interruptor trabaja con los byte, short, char, y inttipos de datos primitivos. También trabaja con tipos enumerados (discutidos en Clases y herencia) y algunas clases especiales que "envolver" ciertos tipos primitivos: Character, Byte, Short, y Integer(discutido en simple de objetos de datos).

Debe haber una buena razón por la que longno se permite el tipo de datos primitivo. ¿Alguien sabe que es?

Fostah
fuente

Respuestas:

52

Creo que, hasta cierto punto, probablemente fue una decisión arbitraria basada en el uso típico del interruptor.

Un cambio se puede implementar esencialmente de dos maneras (o en principio, una combinación): para un pequeño número de casos, o aquellos cuyos valores están ampliamente dispersos, un cambio esencialmente se convierte en el equivalente de una serie de si en una variable temporal (el valor que se enciende solo debe evaluarse una vez). Para un número moderado de casos que son más o menos consecutivos en valor, se usa una tabla de cambio (la instrucción TABLESWITCH en Java), mediante la cual la ubicación a la que saltar se busca de manera efectiva en una tabla.

Cualquiera de estos métodos podría, en principio, utilizar un valor largo en lugar de un número entero. Pero creo que probablemente fue solo una decisión práctica para equilibrar la complejidad del conjunto de instrucciones y el compilador con la necesidad real: los casos en los que realmente necesita cambiar un largo son lo suficientemente raros como para que sea aceptable tener que volver a escribir como un serie de declaraciones IF, o trabajar de alguna otra manera (si los valores largos en cuestión están muy juntos, puede en su código Java cambiar el resultado int de restar el valor más bajo).

Neil Coffey
fuente
Tengo que estar de acuerdo con el argumento de la "rareza", ya que he estado desarrollando en Java por un tiempo y nunca me encontré con una situación en la que necesitaba / intenté encender por mucho tiempo hasta ahora.
Fostah
3
Dimitris, mi razonamiento no se basó en una comprensión intermedia de seguridad de subprocesos, solo que no creo que el argumento de seguridad de subprocesos que presentaste sea válido.
Neil Coffey
13
La decisión de diseño más estúpida de la historia. Tengo un largo que es un montón de banderas, y tengo que llevarlo con if ... else if ... else ... Totalmente ridículo.
m0skit0
2
Estoy de acuerdo con @ m0skit0. En mi caso, alguien más escribió una API que usa longs como constantes, porque eso es lo que almacenan en la base de datos. Ahora no puedo usar un interruptor / caso debido a la mala decisión de otra persona.
CodeChimp
1
No estoy diciendo que sea "el fin del mundo". Y sí, hay formas de evitarlo, pero todas parecen trucos. Mi punto es que, como desarrolladores, siempre debemos tratar de pensar más allá de cómo creemos que la gente usará las cosas que construimos. En este caso, alguien tomó la decisión de no permitir el cambio / caso por mucho tiempo basándose en el pensamiento inicial de que "¿Quién tendría honestamente 2 ^ 64 casos". Quizás eso sea una simplificación excesiva. Tal vez haya alguna otra razón por la que no se pueden cambiar los largos y que simplemente no estábamos al tanto. Para mí, me parece extraño no admitirlo.
CodeChimp
21

Debido a que no implementaron las instrucciones necesarias en el código de bytes y realmente no desea escribir tantos casos, no importa cuán "listo para producción" esté su código ...

[EDITAR: extraído de los comentarios sobre esta respuesta, con algunas adiciones en el fondo]

Para ser exactos, 2³² son muchos casos y cualquier programa con un método lo suficientemente largo como para contener más de eso, ¡será absolutamente horrible! En cualquier idioma. (La función más larga que conozco en cualquier código en cualquier idioma es un poco más de 6k SLOC - sí, es un gran switch- y es muy difícil de manejar.) Si usted está realmente atascado con tener una longdonde se debe tener solo una into menos, entonces tienes dos alternativas reales.

  1. Utilice alguna variante sobre el tema de las funciones hash para comprimir el longen un int. El más simple, solo para usar cuando tienes el tipo incorrecto, ¡es simplemente lanzar! Más útil sería hacer esto:

    (int) ((x&0xFFFFFFFF) ^ ((x >>> 32) & 0xFFFFFFFF))
    

    antes de encender el resultado. También tendrá que averiguar cómo transformar los casos contra los que está probando. Pero realmente, eso sigue siendo horrible, ya que no aborda el problema real de muchos casos.

  2. Una solución mucho mejor si está trabajando con una gran cantidad de casos es cambiar su diseño para usar un Map<Long,Runnable>o algo similar para que esté buscando cómo enviar un valor en particular. Esto le permite separar los casos en varios archivos, lo cual es mucho más fácil de administrar cuando el recuento de casos aumenta, aunque se vuelve más complejo organizar el registro de la gran cantidad de clases de implementación involucradas (las anotaciones pueden ayudar al permitirle crear el código de registro automáticamente).

    FWIW, hice esto hace muchos años (cambiamos al J2SE 1.2 recién lanzado a la mitad del proyecto) cuando construí un motor de código de bytes personalizado para simular hardware masivamente paralelo (no, reutilizar la JVM no habría sido adecuado debido a la radical diferentes modelos de valor y ejecución involucrados) y simplificó enormemente el código en relación con el gran tamaño switchque estaba usando la versión C del código.

Para reiterar el mensaje para llevar a casa, querer switchen a longes una indicación de que tiene los tipos incorrectos en su programa o que está construyendo un sistema con tanta variación involucrada que debería usar clases. Es hora de repensar en cualquier caso.

Becarios Donal
fuente
1
Pero, ¿el rango que se conmuta realmente tiene más de 32 bits de ancho? Nunca había oído hablar de un código que necesitara tantos brazos de cambio. Sin eso, puede compactar el rango (por ejemplo, usando alguna variación en el tema de las funciones hash) para hacer algo que funcione. O use a Map<Long,Runnable>para resolver el problema de una manera completamente diferente. :-)
Donal Fellows
3
@Lord Torgamus: Presumiblemente porque es una tontería. Piénselo por un momento: ¿por qué alguien, cualquiera , tendría un código con más de 2 32 brazos en una switch? Querer elegir entre un conjunto finito de tantos elementos simplemente apunta a un error en el diseño fundamental del programa. El hecho de que la gente lo solicite indica simplemente que ** se equivocaron en el diseño .
Donal Fellows
1
Por cierto, si alguien quiere discutir más sobre esto, comience por dar un caso de uso para encender un archivo. De lo contrario, estaremos atrapados discutiendo con hipotéticos para siempre ...
Donal Fellows
2
@DonalFellows, Otro caso es en Android con ListView's. Si bien listViews puede tener un longnúmero de filas, específicamente solo necesito 4. Este es un caso en el que me han longentregado una mano que indica en qué fila se está actuando, y necesito hacer cosas diferentes según la fila. Supongo que podría lanzar a int, pero me habría facilitado la vida si pudiera haber switcheditado en la variable. Tal como está, solo estoy usando una cadena de ify en su else iflugar.
Christian Smith
7
"Para reiterar el mensaje para llevar a casa, querer encender un largo es una indicación de que tiene los tipos incorrectos en su programa" - ¡No, no lo es! Tener un longno significa que vas a comprobar todas las posibilidades, al igual que tener un into un Stringno significa eso tampoco. Significa que los valores que tiene, que podrían ser pocos, tienen un rango amplio . Podrías estar revisando algunos casos simples y optando defaultpor el resto. Tener que hacer turnos y lanzamientos significa que correrá el riesgo de perder datos. En pocas palabras, es una mala decisión de diseño de Java, no un problema del usuario.
jbx
6

Porque el índice de la tabla de búsqueda debe ser de 32 bits.

JRL
fuente
3
Pero, de nuevo, switchno es necesario implementar una tabla de búsqueda necesariamente.
Joachim Sauer
10
Si ese fuera el caso, nunca podrían implementar el cambio para Strings (como planean actualmente).
Dimitris Andreou
1
@DimitrisAndreou sí, ahora podemos cambiar en Strings: D (desde hace años: P)
J. Doe
-11

Un largo, en arquitecturas de 32 bits, se representa con dos palabras . Ahora, imagine lo que podría suceder si debido a una sincronización insuficiente, la ejecución de la instrucción switch observa un largo con sus 32 bits altos de una escritura y los 32 bajos de otra. Podría intentar ir a ... ¡quién sabe dónde! Básicamente en algún lugar al azar. Incluso si ambas escrituras representaran casos válidos para la declaración de cambio, su combinación divertida probablemente no conduciría ni a la primera ni a la segunda, o, lo que es peor, ¡podría conducir a otro caso válido, pero no relacionado!

Al menos con un int (o tipos menores), no importa lo mal que lo estropees, la instrucción switch leerá al menos un valor que alguien realmente escribió , en lugar de un valor "de la nada".

Por supuesto, no sé la razón real (¡han pasado más de 15 años, no he estado prestando atención tanto tiempo!), Pero si se da cuenta de lo inseguro e impredecible que podría ser tal construcción, estará de acuerdo en que esta es una duda muy buena razón para no siempre tienen un interruptor en posiciones largas (y siempre -pun intended- habrá 32 bits máquinas, eso seguirá siendo válida).

Dimitris Andreou
fuente
1
No creo que esto siga. El valor que se enciende debe calcularse y almacenarse en un registro o en la pila. Si ese valor se calcula en función de los datos a los que acceden varios subprocesos, este cálculo debe ser seguro para subprocesos, sea cual sea el ancho del resultado. Pero luego, una vez que ese resultado está en un registro o en la pila, solo se accede a él mediante el hilo de conmutación y es seguro.
Neil Coffey
Neil, tu argumento es bastante confuso: "Pero entonces, una vez que ese resultado está en un registro o en la pila, solo se accede a él mediante el hilo de conmutación y es seguro". Claro, usar ese valor es seguro para subprocesos. Pero mi punto es que ese valor ya puede ser incorrecto debido a errores de sincronización en el código de usuario . Usar hilo seguro, un valor incorrecto es menos útil :) Este problema nunca se puede eliminar: el código concurrente con errores ya podría haber producido el valor largo incorrecto / "de la nada", que se puede usar posteriormente en el cambio, lo que hace que el cambie a una dirección de caso que nadie haya especificado .
Dimitris Andreou
1
Dimitris, tal vez haya algo en tu argumento que no entiendo. El valor activado podría ser incorrecto debido a errores de sincronización en el código de usuario. Pero no creo que haya nada inherente en la declaración de cambio que haga que esto sea más probable que en otros casos. Y pensándolo bien lo mejor que puedo, no creo que la no atomicidad de las palabras altas / bajas de lecturas / escrituras largas en la memoria sea de hecho un problema. (Pensando en las cosas de otra manera: podría decidir que una comparación if en un largo no estaba permitida basándose en el mismo argumento).
Neil Coffey
Si bien los problemas potenciales con la representación durante mucho tiempo como dos palabras sin escrituras atómicas garantizadas es un problema general, de acuerdo, en el caso de un cambio, sería un peligro aún más pronunciado. Es como enviar un sobre con un mensaje donde la mitad de la dirección es de una persona, la mitad es de otra: la dirección final podría ser válida y corresponder a un tipo totalmente aleatorio que luego recibiría el sobre y actuaría en consecuencia. Una cosa es que la lectura de la basura y la producción de basura (como un valor lógico mal), pero la basura leer y hacer saltos al azar no parecer un poco más peligroso para mí.
Dimitris Andreou
7
Sé que esto es antiguo y comentarlo es algo discutible, pero quiero subrayar que su argumento también se aplica ify el resultado sería igual de malo: resultado incorrecto ~> rama incorrecta tomada. Creación de un largo if- else- ifcadena en lugar de una switchen realidad llevar a exactamente el mismo resultado.
Nicolai Parlog