¿Esta funcionalidad se va a poner en una versión posterior de Java?
¿Alguien puede explicar por qué no puedo hacer esto, como en la forma técnica en que funciona la switch
declaración de Java ?
java
string
switch-statement
Alex Beardsley
fuente
fuente
"Don't hold your breath."
lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179Respuestas:
Las declaraciones de cambio con
String
casos se han implementado en Java SE 7 , al menos 16 años después de que se solicitaron por primera vez. No se proporcionó una razón clara para la demora, pero probablemente tuvo que ver con el rendimiento.Implementación en JDK 7
La característica ahora se ha implementado
javac
con un proceso de "des-azúcar"; una sintaxis limpia y de alto nivel que usaString
constantes en lascase
declaraciones se expande en tiempo de compilación en un código más complejo siguiendo un patrón. El código resultante utiliza instrucciones JVM que siempre han existido.A
switch
conString
casos se traduce en dos interruptores durante la compilación. El primero asigna cada cadena a un número entero único: su posición en el interruptor original. Esto se hace activando primero el código hash de la etiqueta. El caso correspondiente es unaif
declaración que prueba la igualdad de cadena; Si hay colisiones en el hash, la prueba es en cascadaif-else-if
. El segundo conmutador refleja eso en el código fuente original, pero sustituye las etiquetas de los casos con sus posiciones correspondientes. Este proceso de dos pasos facilita la preservación del control de flujo del interruptor original.Interruptores en la JVM
Para obtener más detalles técnicos sobre
switch
, puede consultar la Especificación JVM, donde se describe la compilación de las instrucciones del conmutador . En pocas palabras, hay dos instrucciones JVM diferentes que se pueden usar para un conmutador, dependiendo de la escasez de las constantes utilizadas por los casos. Ambos dependen del uso de constantes enteras para que cada caso se ejecute de manera eficiente.Si las constantes son densas, se usan como un índice (después de restar el valor más bajo) en una tabla de punteros de instrucción: la
tableswitch
instrucción.Si las constantes son escasas, se realiza una búsqueda binaria del caso correcto: la
lookupswitch
instrucción.Al quitar el azúcar
switch
a losString
objetos, es probable que se utilicen ambas instrucciones. Ellookupswitch
es adecuado para el primer interruptor de códigos hash para encontrar la posición original de la caja. El ordinal resultante es un ajuste natural para atableswitch
.Ambas instrucciones requieren que las constantes enteras asignadas a cada caso se ordenen en tiempo de compilación. En tiempo de ejecución, aunque el
O(1)
rendimiento detableswitch
generalmente parece mejor que elO(log(n))
rendimiento delookupswitch
, requiere un análisis para determinar si la tabla es lo suficientemente densa como para justificar la compensación espacio-tiempo. Bill Venners escribió un gran artículo que cubre esto con más detalle, junto con una mirada oculta a otras instrucciones de control de flujo de Java.Antes de JDK 7
Antes de JDK 7,
enum
podría aproximarse a unString
interruptor basado en. Esto utiliza elvalueOf
método estático generado por el compilador en cadaenum
tipo. Por ejemplo:fuente
Pill
algunas medidas basadas enstr
, diría que si no, es preferible ya que le permite manejarstr
valores fuera del rango ROJO, AZUL sin necesidad de detectar una excepciónvalueOf
o verificar manualmente una coincidencia con el nombre de cada tipo de enumeración que solo agrega una sobrecarga innecesaria. En mi experiencia, solo tiene sentido usarlovalueOf
para transformarlo en una enumeración si más adelante se necesitaba una representación segura del tipo de cadena.(hash >> x) & ((1<<y)-1)
produciría valores distintos para cada cadena quehashCode
es diferente y(1<<y)
es menos del doble del número de cadenas (o en al menos no mucho mayor que eso).Si tiene un lugar en su código donde puede activar una Cadena, entonces puede ser mejor refactorizar la Cadena para que sea una enumeración de los posibles valores, que puede activar. Por supuesto, limita los valores potenciales de las cadenas que puede tener a los de la enumeración, que pueden o no ser deseables.
Por supuesto, su enumeración podría tener una entrada para 'otro' y un método fromString (String), entonces podría tener
fuente
El siguiente es un ejemplo completo basado en la publicación de JeeBee, utilizando la enumeración de Java en lugar de utilizar un método personalizado.
Tenga en cuenta que, en Java SE 7 y versiones posteriores, puede utilizar un objeto String en la expresión de la instrucción switch.
fuente
Los conmutadores basados en enteros se pueden optimizar para obtener un código muy eficiente. Los conmutadores basados en otro tipo de datos solo se pueden compilar en una serie de sentencias if ().
Por esa razón, C & C ++ solo permiten cambios en tipos enteros, ya que no tenía sentido con otros tipos.
Los diseñadores de C # decidieron que el estilo era importante, incluso si no había ventaja.
Los diseñadores de Java aparentemente pensaron como los diseñadores de C.
fuente
También
String
se puede mostrar un ejemplo de uso directo desde 1.7:fuente
James Curran dice sucintamente: "Los interruptores basados en números enteros pueden optimizarse a un código muy eficiente. Los interruptores basados en otro tipo de datos solo pueden compilarse en una serie de declaraciones if (). Por esa razón, C & C ++ solo permiten cambios en tipos enteros, ya que no tenía sentido con otros tipos ".
Mi opinión, y es solo eso, es que tan pronto como comience a encender los no primitivos, debe comenzar a pensar en "igual" versus "==". En primer lugar, comparar dos cadenas puede ser un procedimiento bastante largo, que se suma a los problemas de rendimiento que se mencionan anteriormente. En segundo lugar, si se activan las cadenas, habrá demanda para activar las cadenas ignorando mayúsculas y minúsculas, activando las cadenas considerando / ignorando el entorno local, activando las cadenas basadas en expresiones regulares ... aprobaría una decisión que ahorró mucho tiempo para el desarrolladores de lenguaje a costa de una pequeña cantidad de tiempo para los programadores.
fuente
matched
ynot matched
. (Sin embargo, sin tener en cuenta cosas como [nombre] grupos / etc.)Además de los buenos argumentos anteriores, agregaré que mucha gente ve hoy
switch
como un resto obsoleto del pasado procesal de Java (de vuelta a C veces).No comparto completamente esta opinión, creo que
switch
puede tener su utilidad en algunos casos, al menos debido a su velocidad, y de todos modos es mejor que algunas series de números en cascadaelse if
que vi en algún código ...Pero, de hecho, vale la pena mirar el caso en el que necesita un interruptor, y ver si no puede ser reemplazado por algo más OO. Por ejemplo, enumeraciones en Java 1.5+, tal vez HashTable o alguna otra colección (a veces lamento no tener funciones (anónimas) como ciudadanos de primera clase, como en Lua, que no tiene interruptor, o JavaScript) o incluso polimorfismo.
fuente
Si no está utilizando JDK7 o superior, puede usarlo
hashCode()
para simularlo. Debido a queString.hashCode()
generalmente devuelve valores diferentes para cadenas diferentes y siempre devuelve valores iguales para cadenas iguales, es bastante confiable (Las cadenas diferentes pueden producir el mismo código hash que @Lii mencionado en un comentario, como"FB"
y"Ea"
) Consulte la documentación .Entonces, el código se vería así:
De esa manera, técnicamente está activando un
int
.Alternativamente, puede usar el siguiente código:
fuente
case
declaraciones tenían que, siempre pensé, ser valores constantes, yString.hashCode()
no lo son (incluso si en la práctica el cálculo nunca ha cambiado entre JVM).case
valores de las declaraciones no tienen que ser determinables en tiempo de compilación, por lo que funciona finamente.Durante años hemos estado utilizando un preprocesador (n de código abierto) para esto.
Los archivos preprocesados se denominan Foo.jpp y se procesan en Foo.java con un script de hormiga.
La ventaja es que se procesa en Java que se ejecuta en 1.0 (aunque normalmente solo admitimos 1.4). También fue mucho más fácil hacer esto (muchos modificadores de cadena) en comparación con falsificarlo con enumeraciones u otras soluciones: el código era mucho más fácil de leer, mantener y comprender. IIRC (no puede proporcionar estadísticas o razonamiento técnico en este momento) también fue más rápido que los equivalentes naturales de Java.
Las desventajas son que no está editando Java, por lo que es un poco más de flujo de trabajo (editar, procesar, compilar / probar) además de que un IDE se vinculará de nuevo con Java, que es un poco complicado (el interruptor se convierte en una serie de pasos lógicos if / else) y el orden de cambio de caja no se mantiene.
No lo recomendaría para 1.7+, pero es útil si desea programar Java que apunte a JVM anteriores (ya que Joe public rara vez tiene la última instalada).
Puede obtenerlo de SVN o buscar el código en línea . Necesitará EBuild para construirlo tal cual.
fuente
Otras respuestas han dicho que esto se agregó en Java 7 y dio soluciones para versiones anteriores. Esta respuesta trata de responder el "por qué"
Java fue una reacción a las sobrecomplejidades de C ++. Fue diseñado para ser un lenguaje simple y limpio.
String recibió un poco de manejo especial de mayúsculas y minúsculas en el lenguaje, pero me parece claro que los diseñadores estaban tratando de mantener al mínimo la cantidad de tripas especiales y azúcar sintáctica.
encender cadenas es bastante complejo bajo el capó ya que las cadenas no son simples tipos primitivos. No era una característica común en el momento en que Java fue diseñado y realmente no encaja bien con el diseño minimalista. Especialmente porque habían decidido no usar mayúsculas y minúsculas == para cadenas, sería (y es) un poco extraño que las mayúsculas y minúsculas funcionen donde == no lo hace.
Entre 1.0 y 1.4, el lenguaje en sí se mantuvo prácticamente igual. La mayoría de las mejoras a Java estaban en el lado de la biblioteca.
Todo eso cambió con Java 5, el lenguaje se amplió sustancialmente. Otras extensiones siguieron en las versiones 7 y 8. Espero que este cambio de actitud haya sido impulsado por el aumento de C #
fuente
JEP 354: Expresiones de cambio (Vista previa) en JDK-13 y JEP 361: Expresiones de cambio (Estándar) en JDK-14 extenderá la declaración de cambio para que pueda usarse como una expresión .
Ahora usted puede:
case L ->
):Entonces, la demostración de las respuestas ( 1 , 2 ) podría verse así:
fuente
No es muy bonito, pero aquí hay otra forma para Java 6 y más abajo:
fuente
Es una brisa en Groovy; Incrusto el jarro maravilloso y creo una
groovy
clase de utilidad para hacer todas estas cosas y más que encuentro exasperante en Java (ya que estoy atascado usando Java 6 en la empresa).fuente
Cuando uses intellij también mira:
Archivo -> Estructura del proyecto -> Proyecto
Archivo -> Estructura del proyecto -> Módulos
Cuando tenga varios módulos, asegúrese de establecer el nivel de idioma correcto en la pestaña del módulo.
fuente
fuente