2010-01-20 49 views
12

Aprecio que cualquier cosa que se pueda hacer mediante una declaración de conmutación, se puede hacer mediante una instrucción if else.Cuándo utilizar una instrucción switch en Java

Pero, ¿existen reglas estilísticas para cuando se debe usar el conmutador en lugar de if else statment?

+3

Cuando no desea peeve-off la próxima desarrollador que tiene que mantener sus códigos 100x si/otras declaraciones –

+6

No lo sé ... Creo que estaré igual de molesto por 100 casos en un cambio. :-) Huele a mal diseño. – cjstehno

+0

Siempre porque es más rápido: http://stackoverflow.com/questions/2086529 –

Respuesta

15

Bueno, switch se siente "más ligero" en muchos casos que en una escalera if/else if, en mi opinión. Básicamente, no tienes tanta sintaxis con llaves y paréntesis en el camino de tu código. Dicho esto, switch hereda la sintaxis de C. Eso significa que tiene break y solo un ámbito único para las variables a menos que introduzca nuevos bloques.

Aún así, el compilador puede optimizar las declaraciones switch en una tabla de búsqueda y realizar comprobaciones en tiempo de compilación de los literales cuando se trata de enumeraciones. Por lo tanto, sugiero que generalmente es preferible usar switch sobre if/else if si se trata de tipos numéricos o enum.

+2

FWIW, las cadenas en instrucciones de interruptor están en JDK7 – basszero

+0

@basszero: ¿Esto es definitivo? Lo último que sé es que todavía está por decidirse. – BalusC

4

Utiliza una instrucción switch cuando está activando diferentes valores de tipos primitivos/enum/wrapper. (Y no todos los tipos de primitiva/envoltura, solo los compatibles - byte, short, char, int).

If/else se encarga del resto.

Por ejemplo, es más estético que decir:

int i = getValueOfI(); 
switch (i) { 
    case 1: 
    // do something 
    break; 
    case 2: 
    // do something 
    break; 

etc.

que

if (i == 1) { 

} else if (i == 2) { 

}... 

para un gran número de casos. Pero no puede activar Strings, valores de función o condiciones complejas, por lo que si necesita hacer algo de eso, entonces está bloqueado con if/else.

+1

No estoy 100% convencido de que sea estéticamente más agradable, más o menos depende de lo que esté acostumbrado. El problema con el cambio es que es muy fácil olvidar la declaración de interrupción. – Dan

+1

@Dan: Hice la costumbre de escribir siempre el 'case' y' break' en pares. De esa forma, nunca los olvidaré. Pero estoy de acuerdo en que no es muy agradable. Aunque probablemente no haya muchas personas discutiendo en ese frente :-) – Joey

+0

De acuerdo al menos con el if-else usted tiene la sintaxis para ayudarlo, tal vez el valor predeterminado en un cambio debería haber sido romperse después de que se haya emparejado un caso. – Dan

1

En primer lugar, la declaración del interruptor debe ser utilizable. Si el interruptor se basa en el valor de una variable, entonces se puede usar. Si se basa en una expresión booleana AND/OR/NOT compleja que varía para cada condición, entonces no puede usarla en absoluto.

Dicho esto, si es aplicable, y hay al menos 2 casos, entonces utilizo el interruptor. Es más fácil de extender, más fácil de leer y verificar.

0

si tiene demasiadas condiciones para verificar de un tipo similar puede cambiar.

0

Al igual que en otros lenguajes como C o C++, las instrucciones de cambio son útiles cuando desea comparar una variable determinada con una lista de valores posibles y realizar una acción según estos valores. Es más terser que if else declaraciones.

5

Utilizo instrucciones de cambio para enumeraciones, es más legible que un if if if else declaraciones. Sin embargo, debe intentar evitar tales controles en un diseño OO.

2

Hay dos factores para mí:

legibilidad y si desea que decidir algo utilizando rangos de valores o condiciones (en los estados de conmutación sólo se puede utilizar un único valor entero o enumerado).

+0

De acuerdo, cualquier cosa que requiera * rangos * de valores necesita una instrucción 'if'. –

3

Personalmente, uso para encontrar ambos constructos un poco demasiado de procedimiento. Aunque puede considerarse como OO extermismo, uso un Mapa que contiene instancias de una interfaz interna para cada caso del if. Permite un mejor aislamiento del código, creo. Sin embargo, para responder realmente a su pregunta, solo uso los conmutadores cuando tengo casos que realmente se superponen (el, no uso declaraciones de interrupción). Desafortunadamente, realmente no es un bloque de código de mantenimiento.

+0

Estoy completamente de acuerdo con su punto de usar declaraciones de interruptor para código superpuesto. En mi opinión, las declaraciones de cambio requieren una forma completamente diferente de pensar en las declaraciones "if..else if". Utiliza el "caso :" para seleccionar su punto de partida para un caso en un bloque de código, y luego usa el salto para comenzar un bloque de código completamente nuevo. El único problema para esta forma de pensar para mí, es que no puedes tener casos duplicados para cada "bloque de interrupción" –

-1

interruptor tiene dos desventajas importantes:

  • se limita a los tipos primitivos y enumeraciones
  • usted tiene que recordar "ruptura", lo que podría dar lugar a errores no tan obvias

menudo El interruptor es un signo de un diseño pobre de OO, porque es mejor usar polimorfismo.

La única ventaja posible del conmutador es que es más legible. Pero es

switch (i) { 
    case 1: 
    // do something 
    break; 
    case 2: 
    // do something 
    break; 
} 

más legible que esto:

if (i == 1) 
    //do something 
else if (i == 2) 
    // do something else 

diría: ¡no! Y no tendrías las desventajas de cambiar.

Mi sugerencia: intentar evitar el cambio.

+5

. De todos modos, probablemente no harías un cambio solo por dos casos. En lugar de evitar el cambio, aprender a usarlo de manera efectiva es una mejor idea, que es exactamente lo que la pregunta hace. – Armstrongest

+0

Totalmente de acuerdo, si escribe su código OO, encontrará muy poca necesidad de declaraciones de cambio. –

10

interruptor tiene una ventaja cuando se trata de la claridad:

switch (i) { 
    case 1: 
    // do something 
    break; 
    case 2: 
    case 4: 
    // do something 
    break; 
    case 5: 
    // do something 
    break; 
} 

Si el código de 2 y 4 son idénticos, puede ser más claro que:

if (i == 1) { 
    // do something 
} 
if ((i == 2) || (i == 4)) { 
    // do something 
} 
if ((i == 5) { 
    // do something 
} 

También es más fácil para usted (u otro programador) para dividir los casos y .

+1

Por otro lado, necesita deletrear cada valor individual para los casos comunes en lugar de poder escribir 'else if (i% 2 == 0)'. A veces echo de menos 'Select Case' de VB :-) – Joey

1

Conmutador y enumeraciones.

Si el valor enum que está probando puede ser legítimamente null por cualquier razón, al ponerlo en la instrucción switch se generará una NullPointerException. Sin mirar el código de bytes es desconcertante que lo haga.

La explicación: las enumeraciones son azúcar sintáctico introducido en 1.5. La declaración de cambio todavía funciona con las buenas intenciones, pero los valores que utiliza son ordinales asignados a enum. Para obtener el ordinal, el valor enum DEBE ser no nulo.

if statement, por otro lado, estarían encantados de aceptar null por un valor enum y simplemente fallar la prueba sin NPE.

1

Tal vez un poco offtopic, pero si respondí solo la pregunta en el título, entonces diría que no deberías usar el interruptor en todas las situaciones donde los casos representan estados de algún objeto. El patrón de estado es una solución mucho más bonita en esos casos.

3

me gustaría sugerir la simple regla:

Siempre use un switch cuando tenga al menos 2 opciones para diferenciar entre, cuando el tipo de datos es utilizable para un interruptor y cuando todas las opciones tienen valores constantes.

Hay tres buenas razones. Uno, en la mayoría de los casos switch es más rápido que una cascada de if/else. Dos, aclara la intención del código. Tres, el oh tan olvidado break dilema es un mal menor que el enorme if/else cascadas que se rompen accidentalmente porque alguien olvidó un else.

Las máquinas virtuales de Java realmente admiten dos tipos diferentes de conmutadores: las instrucciones de cambio de tabla y de conmutación de búsqueda. El compilador genera el modificador de tablas si todas las constantes case se encuentran en un rango estrecho, de lo contrario genera un conmutador de búsqueda. Para declaraciones grandes switch con muchos casos, el conmutador de tabla es más eficiente que el conmutador de búsqueda. El conmutador de búsqueda generalmente se implementa mediante alguna forma de búsqueda binaria.

+0

Totalmente de acuerdo, excepto por el olvidado 'break' frente al olvidado 'else'. No creo que se pueda argumentar que uno es más probable que el otro – Kirby

1

Siempre he encontrado que la declaración de cambio de java no es tan potente como lo necesito. En su last release lambdaj implements it con un uso inteligente de cierre y Hamcrest matcher. Por ejemplo, lambdaj Switcher permite implementar un patrón de estrategia. Supongamos que tiene que cambiar entre tres algoritmos de clasificación en función de alguna característica de la lista que se va a ordenar. En particular, vamos a suponer que hemos un algoritmo especializado para cuerdas:

public List<String> sortStrings(List<String> list) { 
    // a sort algorithm suitable for Strings 
} 

otro que funciona bien con pequeñas listas que tienen no más de 100 artículos:

public List<T> sortSmallList(List<T> list) { 
    // a sort algorithm suitable for no more than 100 items 
} 

y más general de propósito uno:

public List<String> sort(List<String> list) { 
    // a generic sort algorithm 
} 

Dados estos 3 métodos de clasificación, es posible crear una estrategia que elija la más adecuada de ellas de la siguiente manera declarativa:

Switcher<List<T>> sortStrategy = new Switcher<List<T>>() 
    .addCase(having(on(List.class).get(0), instanceOf(String.class))), 
     new Closure() {{ of(this).sortStrings(var(List.class)); }}) 
    .addCase(having(on(List.class).size(), lessThan(100))), 
     new Closure() {{ of(this).sortSmallList(var(List.class)); }}) 
    .setDefault(new Closure() {{ of(this).sort(var(List.class)); }}); 

y ordenar una lista con el mejor algoritmo disponible mediante la invocación del Switcher:

List<T> sortedList = sortStrategy.exec(list, list); 
+0

+1: Esto se ve bien. Me sorprende que la declaración de cambio de Java todavía sea tan débil: ni siquiera puedes encender un Entero. La expresión del partido de Scala es mucho más poderosa. Heck, la declaración EVALUAR de Cobol (19) 85 es sustancialmente más útil. –

1

La respuesta depende de qué es exactamente lo que está haciendo, así como la distribución de las opciones.

Si una condición es dominante, entonces el if/then es apropiado.

if (i == 1){ 
    //do something 
}else if (i == 2){ 
    // do something else 
} 

Si las condiciones se distribuyen uniformemente, la optimización en el compilador proporcionará una ventaja de rendimiento. Esta diferencia de rendimiento se vuelve más pronunciada a medida que aumenta el número de opciones posibles.

switch (i) { 
    case 1: 
    // do something 
    break; 
    case 2: 
    // do something else 
    break; 
    .... 
    case N: 
    // do yet something else 
    break; 
    } 

Dicho esto, si el rendimiento no es importante ir con el que alguna vez se acercan prefiere ya que la mayoría legible (mantenible y más fácil de escribir).

Si, por el contrario, si su código está en un punto de acceso donde el rendimiento ES importante, debe ir con el interruptor.

Para condiciones "extremadamente grandes", el ejemplo de Mario del conmutador lambdaj es realmente genial y si se tiene cuidado en la inicialización se obtendrá un rendimiento muy alto. Es una codificación muy similar a la que está generando el optimizador.Definiría "extremadamente grande" como cuando el número de opciones es lo suficientemente grande o complejo como para que valga la pena escribir todo eso, y vale la pena la confusión de soporte cuando un seguidor desarrollador está tratando de pasar por el código. (¡Comenta tu código con por qué tienes todo eso!).

0

Estoy de acuerdo con x4u's answer.

Además hay otra instancia no mencionado, sin embargo, donde creo que es mejor utilizar if - else bloques en lugar de una switch: cuando las condiciones adicionales en cada uno de los bloques case se prueban con if bloques. Veo esta mezcla todo el tiempo en el código existente.

Por ejemplo, me encontré con este código que tiene una switch en una cadena type y luego comprueba una segunda cadena extension utilizando un if en ambos case declaraciones.

public abstract class Temp { 

    boolean valid; 

    public Temp() { 
     String type = getType(); 
     String extension = getFilenameExtension(); 
     switch(type) { 
      case "Image File": { 
       if(!".jpg".equals(extension) && !".png".equals(extension)) { 
        warnWrongImageFormat(); 
        valid = false; 
       } 
       break; 
      } 
      case "Zip File": { 
       if(!".zip".equals(extension)) { 
        warnWrongZipFormat(); 
        valid = false; 
       } 
       break; 
      } 
      default: { 
       valid = true; 
       break; 
      } 
     } 
    } 

    abstract String getType(); 
    abstract String getFilenameExtension(); 
    abstract void warnWrongImageFormat(); 
    abstract void warnWrongZipFormat(); 
} 

En cambio, es mucho más limpio y menos complejo para reducir esto a uno ifelse

public abstract class Temp { 

    boolean valid; 

    public Temp() { 
     String type = getType(); 
     String extension = getFilenameExtension(); 
     valid = true; 
     if("Image File".equals(type) && !".jpg".equals(extension) && !".png".equals(extension)) { 
      warnWrongImageFormat(); 
      valid = false; 
     } 
     else if("Zip File".equals(type) && !".zip".equals(extension)) { 
      warnWrongZipFormat(); 
      valid = false; 
     } 
    } 

    abstract String getType(); 
    abstract String getFilenameExtension(); 
    abstract void warnWrongImageFormat(); 
    abstract void warnWrongZipFormat(); 
} 
Cuestiones relacionadas