É necessário adicionar o caso padrão ao usar casos de opção?


41

Durante uma recente revisão de código, fui solicitado a colocar defaultcasos em todos os arquivos onde quer que o switchbloco seja usado, mesmo que não haja nada para fazer default. Isso significa que eu tenho que colocar o defaultcaso e não escrever nada nele.

Esta é a coisa certa a fazer? Que finalidade isso serviria?


9
Cabe ao seu ... quero dizer estilo de código do supervisor.
SD

1
Eu acho que existem casos em que você não quer um caso padrão. Considere mudar um enum. Sua opção define um caso para todos os valores possíveis. Agora, adicionar um valor a uma enum deve resultar na adição de um caso nessa opção. Se você não fez isso, isso pode ser um erro, pois você deseja fazer algo para esse novo valor de enumeração, mas seu código não. Seu compilador pode avisá-lo (eu acho; não tenho certeza sobre Java) sobre isso, mas apenas se você não colocou um caso padrão. Como alternativa, você pode imprimir um aviso no caso padrão quando tiver certeza de que o código nunca o alcança.
leemes

6
Eu e meus amigos chamamos isso de caso "Exceção ao desenvolvedor" - quando algo no código acontece que você sabe que nunca deveria acontecer (por motivos de lógica / código), adicionamos uma "Exceção ao desenvolvedor" lançada. Dessa forma, se isso acontecer, uma exceção de desenvolvedor será lançada e, pelo menos, sabemos onde as coisas deram errado.
8133 Marco Marco

Respostas:


31

Parece que existem três casos em que uma defaultdeclaração não é necessária:

  1. não restam outros casos, porque há um conjunto limitado de valores que inserem o switch case. Mas isso pode mudar com o tempo (intencionalmente ou acidentalmente), e seria bom ter um default casese algo mudar _ você pode registrar ou avisar o usuário sobre um valor errado.

  2. você sabe como e onde o valor switch caseserá usado e quais valores entrarão nele. Novamente, isso pode mudar e pode ser necessário um processamento extra.

  3. outros casos não precisam de nenhum processamento especial. Se for esse o caso, acho que você deve adicionar um default case, porque é um estilo de codificação aceito e torna seu código mais legível.

Os dois primeiros casos são baseados em suposições. Então (supondo que você trabalhe em uma equipe não tão pequena, pois você tem revisões regulares de código), não pode se dar ao luxo de fazer essas suposições. Você não sabe quem trabalhará com seu código ou fará chamadas para funções / métodos de chamada em seu código. Da mesma forma, pode ser necessário trabalhar com o código de outra pessoa. Ter o mesmo estilo de codificação tornará mais fácil lidar com o código de alguém (incluindo o seu).


14
Nos casos 1 e 2, um caso padrão deve ser um erro. Se o seu estilo de codificação sempre usar o padrão, faça-o, mas faça com que ele emita um erro em 1 e 2. É possível, ele deve ser emitido em tempo de compilação, mas isso nem sempre é possível, se alguma vez em java. Eu, pelo menos acho que é importante para o usuário para obter a mensagem de que "Você está atirando no próprio pé, passando nesse valor"
martiert

11
Um caso padrão é sempre uma boa idéia, porque, por exemplo, no caso de uma enumeração, se alguém adicionar uma nova entrada à enumeração, seu caso padrão deve fazer algo semelhante ao throw new IllegalStateException("Unrecognised case "+myEnum)que mostra onde e qual é o erro.
Rich

Isso significa que você deve sempre ter uma elsecláusula depois de cada, if/else ifmesmo que saiba que isso não deveria acontecer? Não parece uma boa idéia para mim ...
jadkik94

2
Se posso adicionar uma anedota do meu trabalho: eu tinha um método de fábrica que usava um enum para instanciar algumas classes. Eu havia deixado instruções de que, sempre que você adicionasse uma nova classe a esse projeto, seria necessário adicionar um valor à enumeração e um caso à alternância. Obviamente, uma semana depois que fechei o código, ele parou de funcionar, com uma das exceções que nunca devem ser levantadas. Vou até o programador e pergunto a ele: "você adicionou um caso ao comutador?" e certamente o suficiente, ele não fez. Naquele dia, mudei a exceção para dizer "se você receber esta mensagem, culpe o último desenvolvedor que tocou no código".
Ziv

28

Esta é a coisa certa a fazer? Que finalidade isso serviria?

Não é incomum que os padrões de codificação da empresa exijam um caso padrão para todas as switchdeclarações. Uma razão para isso é que facilita para os leitores encontrar o fim do switch. Outra razão, provavelmente melhor, é que isso faz você pensar por um segundo sobre o que seu código deve fazer quando a condição não corresponde às suas expectativas. Independentemente do motivo do requisito, se for um padrão da empresa, você deve segui-lo, a menos que haja um motivo hermético para não fazê-lo.

Se você acredita que switchinclui casos para todas as condições possíveis, uma boa coisa a fazer é colocar uma assertdeclaração no caso padrão. Dessa forma, quando alguém altera o código e adiciona inadvertidamente uma condição que a sua switchnão cobre, eles acessam asserte percebem que precisam endereçar essa parte do código.

Se você switchcobrir apenas algumas das condições possíveis, mas nada de especial for necessário para as outras, você poderá deixar o caso padrão em branco. É uma boa ideia adicionar um comentário nesse caso para indicar que o caso padrão está intencionalmente vazio porque as condições que o atingem não precisam de nenhum trabalho a ser feito.


1
o que é um assert?
FLY

1
@FLY É uma palavra-chave em Java e, geralmente, uma macro em C, C ++, etc. De qualquer forma, uma declaração é usada para testar se alguma suposição permanece verdadeira e disparar e exceção, ou causar um erro, caso contrário.
Caleb

Eu editei o meu comentário anterior com um link para a Wikipedia
FLY

Você não deve deixar uma caixa vazia. Se o seu switch não cobrir todas as condições possíveis, você deve afirmar as outras condições no seu caso padrão pelo mesmo motivo sugerido para adicionar uma afirmação no caso padrão no seu segundo parágrafo.
precisa saber é o seguinte

12

Se você "ativar" o tipo de enumeração pura, é perigoso ter um fallback padrão. Quando você adiciona valores posteriormente ao tipo de enumeração, o compilador destacará opções com novos valores não cobertos. Se você tiver uma cláusula padrão, o compilador permanecerá silencioso e você poderá perdê-la.


1
+1 para detectar o problema no tempo de compilação e não no tempo de execução.
precisa saber é o seguinte

Gostaria de saber como a resposta completamente enganosa é votada e aceita. A pesquisa rápida mostra que o compilador java tem esse tipo de aviso. Por que diabos você vai esperar por erro em tempo de execução, pessoal?
Artur

Eu não sabia sobre esse comportamento. Meu exemplo de código ideone ideone.com/WvytWq sugere o contrário. Supondo que você diga que é verdade, o que acontece se alguém adiciona valores a um tipo de enumeração, mas você não recompila seu código?
Emory


1
Obviamente, novos valores simplesmente não são tratados pela instrução switch sem o caso padrão. Mas se você não recompilar suas implementações quando as interfaces mudarem, seu sistema de construção estará realmente quebrado.
Artur

9

Em muitos aspectos, esta pergunta é o mesmo que o pedido frequentemente que preciso de uma elsecláusula no final de um if/ else ifescada que cobre todas as opções .

A resposta é, sintaticamente falando, não, você não. Mas há um porém ...

Uma defaultcláusula pode estar lá por (pelo menos) dois motivos:

  1. Como manipulador de erros - com certeza, nunca deve chegar aqui! é uma reivindicação que pode ser feita, mas e se acontecer? A corrupção de dados ou, pior ainda, a validação de dados não é uma rota para a falha do programa, se não for capturado adequadamente. Nesse caso, não deve ser uma cláusula vazia!
  2. Cobertura de Design / Código - mesmo na forma mais simples de um fluxograma, existem duas rotas a partir de uma instrução if, e sempre a otherwisepartir de um caso. Não há razão alguma para não incluí-las no código fonte.

Minha filosofia é sempre bastante simples - avalie o pior cenário das duas opções e opte pelo mais seguro. No caso de uma cláusula vazia elseou default, as piores opções são:

  • Inclua-os: três ou quatro linhas extras de código "redundante".
  • Não os inclua: Dados não autorizados ou uma condição inesperada não ficam presos, potencialmente causando falha no programa.

Dramático demais? Talvez ... mas então meu software tem o potencial de matar pessoas se der errado. Prefiro não correr esse risco.


Como um aparte, as diretrizes da MISRA-C {ver perfil para afiliação} recomendam uma defaultcláusula para cadaswitch


Não apenas no final de uma escada. A questão é: eu preciso de um elsedepois de um if. Mas como você disse, não, não é.
ott--

Como manipulador de erros, sugiro um formulário ligeiramente modificado que evita o padrão vazio. Quanto a matar pessoas, eu não acho que isso realmente importe - as pessoas estarão mortas se você tiver ou não a cláusula padrão, mas facilitará descobrir por que elas morreram.
Emory

Bom ponto, @emory - que não era muito clara, foi isso ... editado :)
Andrew

6

O Java não força você a ter uma declaração 'padrão', mas é uma boa prática ter uma o tempo todo, mesmo que o código nunca possa ser alcançado (agora). Aqui estão alguns motivos:

Por ter uma cláusula padrão inacessível, você mostra ao leitor do seu código que você considerou os valores e sabe o que está fazendo. Você também permite alterações futuras, digamos, por exemplo: um novo valor de enum é adicionado, o comutador não deve ignorar silenciosamente o novo valor; você pode lançar uma exceção lá ou fazer outra coisa.

Para capturar um valor inesperado (caso você não esteja ativando uma enumeração) que é passado - pode ser maior ou menor do que o esperado, por exemplo.

Para lidar com ações 'padrão' - onde os comutadores são para comportamento especial. Por exemplo, uma variável pode ser declarada fora do comutador, mas não inicializada e cada caso a inicializa para algo diferente. O padrão, nesse caso, pode inicializá-lo para um valor padrão, para que o código imediatamente após a alternância não erro / lance uma exceção.

Mesmo que você decida não colocar nada no padrão (sem exceções, registros etc.), mesmo um comentário para dizer que você considerou o fato de que o padrão nunca ocorrerá pode ajudar na legibilidade do seu código; mas isso se resume à preferência pessoal.


2

Eu também acrescentaria que isso depende da filosofia do idioma que você está usando. Em Erlang, por exemplo, onde é uma abordagem comum "deixar travar", você não definiria um caso padrão, mas deixaria sua casedeclaração gerar um erro se ocorrer uma situação que não foi atendida.


0

Você deve sempre ter um padrão, a menos que tenha provada e permanentemente coberto todos os casos. Não custa nada, e é seguro contra falhas em manter sua declaração de troca em um programa em evolução.

Se você realmente tem certeza de que não se importa com nenhum outro caso, o padrão: break; // não se importa, mas o padrão padrão deve ser algo como o padrão: throw new Error ("valor inesperado");

Da mesma forma, você nunca deve "mostrar" uma construção de caso não trivial sem adicionar um comentário ao efeito que pretende mostrar. O Java errou este no estágio de design.


-2

Considerar

enum Gender
{
       MALE ,
       FEMALE ;
}

e

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

Eu diria que isso é superior a ter um caso MALE, um caso FEMALE e um caso padrão que gera uma exceção.

Durante a fase de teste, o computador verificará se g é FÊMEA. Durante a produção, a verificação será omitida. (Sim, uma micro otimização!)

Mais importante, você manterá sua meta de 100% de cobertura de código. Se você escreveu um caso padrão que lança uma exceção, como você a testaria?


1
1. Não há necessidade de micro-otimização - a eliminação do código morto é usada nos compiladores nos últimos 30 anos ou mais e será eliminada pelo JIT. 2. A cobertura de código de 100% geralmente não é considerada importante (por um lado, a cobertura de 100% pode não captar todos os casos extremos, por outro, nenhum teste captura linhas assert_not_reached no programa correto). Nesse caso, não deixar nenhum caso padrão causaria erro do compilador. Por outro lado - como você verificaria se a declaração funcionava (você muda o problema e o oculta por 100% de cobertura, em vez de ter a mesma linha 'isso não vai acontecer, prometo').
Maciej Piechotka

1
3. Quando Gender . FEMALE . equals ( FEMALE ) ;avalia para false? Você quis dizer, Gender.FEMALE.equals (g)mas como você pode depender de referências a enumerações estáveis, basta escrever g == Gender.FEMALE. 4. (Opinião pessoal) esse código é muito mais difícil de ler e produzirá erros um pouco mais confusos.
Maciej Piechotka

@MaciejPiechotka good catch about g. Sim, a microoptimização não é um benefício, mas também não é uma desvantagem. 100% de cobertura de código é um benefício se esse for um dos seus objetivos e sua ferramenta de cobertura de código não verificar declarações (o que não deveria).
Emory

Então, por que esse seria um dos meus objetivos? A parte 'interessante' deve ser testada para todos os casos extremos (independentemente de serem mapeados para linhas de código) e as partes 'chatas' podem ser simplesmente ignoradas para economizar tempo nos testes. A cobertura do código IMHO é uma métrica ruim dos testes.
Maciej Piechotka
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.