¿Es necesario agregar el caso predeterminado mientras se usan los casos de cambio?

41

Durante una revisión reciente del código, se me pidió que pusiera defaultcasos en todos los archivos dondequiera switchque se use el bloque, incluso si no hay nada que hacer default. Eso significa que tengo que poner el defaultcaso y no escribir nada en él.

¿Es esto lo correcto? ¿Qué propósito tendría?

Ankit
fuente
99
Depende de su ... Me refiero al estilo de código del supervisor.
SD
1
Creo que incluso hay casos en los que no desea un caso predeterminado. Considere cambiar una enumeración. Su conmutador define un caso para todos los valores posibles. Ahora, agregar un valor a una enumeración debería resultar en agregar un caso en ese interruptor. Si no lo hizo, esto podría ser un error, ya que desea hacer algo para este nuevo valor de enumeración, pero su código no lo hace. Su compilador puede advertirle (creo; no estoy seguro de Java) sobre eso, pero solo si no puso un caso predeterminado. Alternativamente, puede imprimir una advertencia en el caso predeterminado cuando esté seguro de que el código nunca lo alcanza.
Leemes
66
Mis amigos y yo llamamos a esto el caso de "Excepción de desarrollador": cuando sucede algo en el código que usted sabe que nunca debería suceder (por razones lógicas / de código), agregamos una "Excepción de desarrollador" que se genera. De esa manera, si esto sucede, se produce una excepción de desarrollador y al menos sabemos dónde las cosas salieron muy mal.
Marco

Respuestas:

31

Parece que hay tres casos cuando una defaultdeclaración no es necesaria:

  1. no quedan otros casos, porque hay un conjunto limitado de valores que ingresan al switch case. Pero esto podría cambiar con el tiempo (intencionalmente o accidentalmente), y sería bueno tener un cambio default casesi algo cambia _ podría registrar o advertir al usuario sobre un valor incorrecto.

  2. usted sabe cómo y dónde switch casese usará y qué valores ingresarán. De nuevo, esto podría cambiar y podría ser necesario un procesamiento adicional.

  3. otros casos no necesitan ningún procesamiento especial. Si este es el caso, creo que se le pide que agregue un default case, porque es un estilo de codificación aceptado y hace que su código sea más legible.

Los dos primeros casos se basan en suposiciones. Entonces (suponiendo que trabaje en un equipo no tan pequeño ya que tiene revisiones de código regulares), no puede permitirse hacer esas suposiciones. No sabe quién estará trabajando con su código o haciendo llamadas a funciones / invocando métodos en su código. Del mismo modo, es posible que deba trabajar con el código de otra persona. Tener el mismo estilo de codificación facilitará el manejo del código de alguien (incluido el suyo).

superM
fuente
14
En los casos 1 y 2, un caso predeterminado debería ser un error. Si su estilo de codificación siempre usa el predeterminado, hágalo, pero haga que emita un error en 1 y 2. Es posible, debería emitirlo en tiempo de compilación, pero eso no siempre es posible, si alguna vez en Java. Al menos creo que es importante que el usuario reciba el mensaje de que "se está disparando en el pie al pasar este valor"
Martiert
11
Un caso predeterminado siempre es una buena idea, porque, por ejemplo, en el caso de una enumeración, si alguien agrega una nueva entrada a la enumeración, su caso predeterminado debería hacer algo similar a lo throw new IllegalStateException("Unrecognised case "+myEnum)que le muestra dónde y cuál es el error.
Rico
¿Esto significa que siempre debe tener una elsecláusula después de cada, if/else ifincluso si sabe que eso no debería suceder? No me parece una buena idea ...
jadkik94
2
Si puedo agregar una anécdota de mi trabajo: tenía un método de fábrica que utilizaba una enumeración para crear instancias de algunas clases. Había dejado instrucciones de que cada vez que agregaba una nueva clase a ese proyecto tenía que agregar un valor a la enumeración y un caso al interruptor. Por supuesto, una semana después de que cerré el código, deja de funcionar, con una de las excepciones que nunca deben plantearse. Me acerco al programador y le pregunto: "¿agregó un caso al interruptor?" y seguramente no lo hizo. En ese día cambié la excepción para decir "si recibe este mensaje, culpe al último desarrollador que tocó el código".
Ziv
28

¿Es esto lo correcto? Qué propósito tendría ?

No es raro que los estándares de codificación de la empresa requieran un caso predeterminado para todas las switchdeclaraciones. Una razón para ello es que facilita a los lectores encontrar el final de la switch. Otra razón, probablemente mejor, es que te hace pensar por un segundo sobre lo que debe hacer tu código cuando la condición no coincide con tus expectativas. Independientemente de la razón del requisito, si es un estándar de la compañía, debe seguirlo a menos que haya una razón hermética para no hacerlo.

Si cree que switchincluye casos para cada condición posible, entonces una buena cosa es hacer una assertdeclaración en el caso predeterminado. De esa manera, cuando alguien cambia el código y, sin darse cuenta, agrega una condición que switchno cubre, tocará el botón asserty se dará cuenta de que necesita abordar esa parte del código.

Si switchsolo cubre algunas de las condiciones posibles, pero no hay que hacer nada especial para las demás, puede dejar el caso predeterminado vacío. Es una buena idea agregar un comentario en ese caso para indicar que el caso predeterminado está vacío intencionalmente porque las condiciones que lo afectan no necesitan ningún trabajo.

Caleb
fuente
1
¿Qué es un assert?
VUELO el
1
@FLY Es una palabra clave en Java y, por lo general, una macro en C, C ++, etc. De cualquier manera, se utiliza una afirmación para probar que alguna suposición sigue siendo cierta y arroja una excepción o, de lo contrario, causa un error si no lo es.
Caleb
He editado mi comentario anterior con un enlace a la wikipedia
VUELO el
No debes dejar una caja vacía. Si su cambio no cubre todas las condiciones posibles, debe afirmar las otras condiciones en su caso predeterminado por la misma razón que sugirió agregar una afirmación en el caso predeterminado en su segundo párrafo.
rold2007
12

Si "activa" el tipo de enumeración pura, es peligroso tener una reserva predeterminada. Cuando luego agregue valores al tipo de enumeración, el compilador resaltará los interruptores con nuevos valores no cubiertos. Si tiene una cláusula predeterminada allí, el compilador permanecerá en silencio y es posible que se lo pierda.

Artur
fuente
1
+1 para detectar el problema en tiempo de compilación y no en tiempo de ejecución.
SylvainD
Me pregunto cómo se vota y acepta la respuesta completamente engañosa. Google rápido muestra que el compilador de Java tiene ese tipo de advertencia. ¿Por qué demonios esperarán un error en tiempo de ejecución, amigos?
Artur
No sabía sobre este comportamiento. Mi ejemplo de código ideone ideone.com/WvytWq sugiere lo contrario. Suponiendo que usted dice que es cierto, ¿qué sucede si alguien agrega valores a un tipo de enumeración, pero no recompila su código?
emory
1
Obviamente, los nuevos valores simplemente no se manejan mediante la instrucción switch sin el caso predeterminado. Pero si no recompila sus implementaciones cuando cambian las interfaces, su sistema de compilación está realmente dañado.
Artur
9

En muchos aspectos, esta pregunta es la misma que la pregunta frecuente ¿Necesito una elsecláusula al final de un if/ else ifladder que cubra todas las opciones ?

La respuesta es, sintácticamente hablando, no, no lo haces. Pero hay un sin embargo ...

Una defaultcláusula puede estar allí por (al menos) dos razones:

  1. Como un controlador de errores, claro, ¡ nunca debería llegar aquí! es un reclamo que se puede hacer, pero ¿y si lo hace? La corrupción de datos, o peor aún, la falta de validación de datos, son rutas para fallar en el programa si no se atrapa adecuadamente. En este caso, ¡no debería ser una cláusula vacía!
  2. Cobertura de diseño / código: incluso en la forma más simple de un diagrama de flujo, hay dos rutas desde una declaración if, y siempre otherwisedesde un caso. No hay ninguna razón para no incluirlos en el código fuente.

Mi filosofía siempre es bastante simple: evaluar el peor de los casos de las dos opciones y elegir el más seguro. En el caso de una cláusula elseo una defaultcláusula vacía , las peores opciones son:

  • Inclúyalos: Tres o cuatro líneas adicionales de código "redundante".
  • No incluirlos: los datos no autorizados o una condición inesperada no quedan atrapados, lo que puede provocar un error del programa.

¿Sobredramático? Tal vez ... pero mi software tiene el potencial de matar gente si sale mal. Prefiero no correr ese riesgo.


Además, las pautas de MISRA-C {ver perfil para afiliación} recomiendan una defaultcláusula para cadaswitch

Andrés
fuente
No solo al final de una escalera. La pregunta es: ¿necesito un elsedespués de un if. Pero como dijiste, no, no lo es.
ott--
Como controlador de errores, sugiero una forma ligeramente modificada que evite el valor predeterminado vacío. En cuanto a matar gente, no creo que realmente importe: la gente estará igual de muerta si tienes o no la cláusula predeterminada, pero será más fácil averiguar por qué murieron.
emory
Buen punto, @emory - eso no estaba muy claro, fue ... editado :)
Andrew
6

Java no lo obliga a tener una declaración 'predeterminada', pero es una buena práctica tenerla todo el tiempo, incluso si el código nunca se puede alcanzar (en este momento). Aquí hay algunas razones:

Al tener una cláusula predeterminada inalcanzable, le muestra al lector de su código que consideró los valores y sabe lo que está haciendo. También permite futuros cambios, por ejemplo: si se agrega un nuevo valor de enumeración, el interruptor no debe ignorar silenciosamente el nuevo valor; puedes lanzar una Excepción allí o hacer otra cosa.

Para atrapar un valor inesperado (en caso de que no esté activando una enumeración) que se pasa, puede ser mayor o menor de lo que esperaba, por ejemplo.

Para manejar acciones 'predeterminadas', donde los interruptores son para un comportamiento especial. Por ejemplo, una variable puede declararse fuera del conmutador pero no inicializarse y cada caso la inicializa en algo diferente. El valor predeterminado en este caso podría inicializarlo a un valor predeterminado para que el código inmediatamente después del cambio no produzca un error / arroje una excepción.

Incluso si decide no poner nada en el valor predeterminado (sin excepciones, registros, etc.), incluso un comentario para decir que ha considerado que el hecho de que nunca ocurrirá un valor predeterminado puede ayudar a la legibilidad de su código; Pero eso se reduce a la preferencia personal.

Deco
fuente
2

También agregaría que depende de la filosofía del lenguaje que esté utilizando. En Erlang, por ejemplo, donde es un enfoque común para "dejar que se bloquee", no definiría un caso predeterminado, pero dejaría que su casedeclaración generara un error si se produce una situación que no fue atendida.

Rodrigue
fuente
0

Siempre debe tener un valor predeterminado a menos que haya cubierto de manera comprobable y permanente todos los casos. No cuesta nada y es un seguro contra el incumplimiento de mantener su declaración de cambio en un programa en evolución.

Si está realmente seguro de que no le importan otros casos, el valor predeterminado: break; // no me importa, pero el valor predeterminado predeterminado debería ser algo así como predeterminado: arrojar un nuevo error ("valor inesperado");

Del mismo modo, nunca debe "omitir" una construcción de caso no trivial sin agregar un comentario al efecto que desea omitir. Java se equivocó en la etapa de diseño.

ddyer
fuente
-2

Considerar

enum Gender
{
       MALE ,
       FEMALE ;
}

y

void run ( Gender g )
{
      switch ( g )
      {
           case MALE :
                 // code related to males
                 break ;
           default :
                 assert Gender . FEMALE . equals ( g ) ;
                 // code related to females
                 break ;
      }
}

Yo diría que esto es superior a tener un caso MALE, un caso FEMENINO y un caso predeterminado que arroja una excepción.

Durante la fase de prueba, la computadora verificará si g es FEMENINO. Durante la producción, se omitirá el cheque. (Sí, una microoptimización!)

Más importante aún, mantendrá su objetivo de 100% de cobertura de código. Si escribiste un caso predeterminado que arroja una excepción, ¿cómo lo probarías?

emory
fuente
1
1. No hay necesidad de microoptimalización: la eliminación de código muerto se utiliza en compiladores durante los últimos 30 años y será eliminada por JIT. 2. La cobertura del 100% del código generalmente no se considera importante (por un lado, el 100% de cobertura podría no capturar todos los casos límite, por otro lado, ninguna prueba capturará las líneas afirmar_no_alcanzadas en el programa correcto). En este caso, no dejar ningún caso predeterminado provocaría un error del compilador. Por otro lado, ¿cómo verificaría si la afirmación funciona (cambia el problema y lo oculta al 100% de cobertura en lugar de tener la misma línea 'no sucederá, lo prometo').
Maciej Piechotka
1
3. ¿Cuándo Gender . FEMALE . equals ( FEMALE ) ;evalúa a false? Gender.FEMALE.equals (g)Querías decir, pero como puedes confiar en que las referencias a las enumeraciones sean estables, solo puedes escribir g == Gender.FEMALE. 4. (Opinión personal) dicho código es mucho más difícil de leer y producirá errores un poco más confusos.
Maciej Piechotka
@MaciejPiechotka buena captura sobre g. Sí, la microoptimización no es beneficiosa, pero tampoco es un inconveniente. La cobertura del 100% del código es un beneficio si ese es uno de sus objetivos y su herramienta de cobertura del código no verifica las afirmaciones (que no debería).
emory
Entonces, ¿por qué sería uno de mis objetivos? La parte 'interesante' debe probarse para todos los casos extremos (independientemente de si se asignan a líneas de código) y las partes 'aburridas' pueden saltarse simplemente para ahorrar tiempo en las pruebas. La cobertura del código IMHO es una mala métrica de las pruebas.
Maciej Piechotka