Como lidar com exceções verificadas que nunca podem ser lançadas

35

Exemplo:

foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");

Como a codificação é codificada e correta, o construtor nunca lançará a UnsupportedEncodingException declarada na especificação (a menos que a implementação do java esteja quebrada, caso em que estou perdida). De qualquer forma, o Java me obriga a lidar com essa exceção de qualquer maneira.

Atualmente, parece que

try {
    foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");
}
catch(UnsupportedEncodingException e) { /* won't ever happen */ }

Alguma idéia de como torná-lo melhor?

user281377
fonte
4
Para um desafio real, escreva um teste de unidade para garantir que essa captura realmente funcione.
Jay Elston
11
`throw new ImpossibleException (" Reinicie o universo. As coisas estão bagunçadas ", e);
precisa
11
"Isso nunca vai acontecer" será ...
Thorbjørn Ravn Andersen: após alterar o programa, pode ser que, após pelo menos no seu estado atual, nenhum conjunto de dados de entrada possa acionar a exceção.
user281377
No seu exemplo específico acima, uma exceção é lançada porque o método não sabe se será chamado com uma codificação suportada ou não. Uma maneira de contornar isso seria se houvesse outro construtor que assumisse que um conjunto de caracteres padrão foi fornecido, que não precisaria declarar que uma exceção seria lançada.
Jordan

Respostas:

27

Meu hábito é, apenas estar do lado seguro, colocar um assertpedaço no bloco de captura. Alguém pode alterar o conteúdo do trybloco mais tarde, e você deseja saber se o código falhar, não é?

Péter Török
fonte
Boa ideia; um simples assert false;não adiciona muita confusão e deixa claro que eu suponho que o bloco catch nunca será inserido.
user281377
5
@ammoQ, ou você pode até mesmo adicionar uma mensagem para tornar a sua intenção absolutamente claro: assert false : "should never happen".
Péter Török
13
Melhor ainda: "Nunca deve acontecer porque o Java exige que o conjunto de caracteres ISO-8859-1 seja suportado."
dan04
3
assertimplica que asserções estão ativadas. Eu jogo um UnexpectedException(que também tem o benefício de me deixar rastrear a pilha ...).
Pique
35

Se eu recebesse um centavo por cada vez que vi um log / erro, "Isso nunca deveria acontecer", eu teria ... bem, dois centavos. Mas ainda...

Blocos de captura vazios fazem meus sentidos de aranha formigarem e a maioria das boas ferramentas de análise de código reclama. Eu evitaria a todo custo deixá-los vazios. Claro, agora você sabe que o erro nunca pode acontecer, mas daqui a um ano alguém substituirá a pesquisa global "ISO-8859-1" e, de repente, você poderá ter um erro extremamente difícil de encontrar.

A assert falsesugestão é boa, mas como as asserções podem ser desabilitadas no tempo de execução, elas não são garantia. Eu usaria um em RuntimeExceptionvez disso. Eles não precisarão ser pegos chamando classes e, se ocorrerem, você terá um rastreamento de pilha para fornecer informações completas.

Fredrik
fonte
28

Eu sempre fiz assim:

try {
    foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");
} catch(UnsupportedEncodingException e) {
    throw new AssertionError(e);
}

Pode ser um pouco detalhado (Java é ...), mas pelo menos você receberá um erro de afirmação quando o impossível acontecer.

Se a implementação do Java estiver interrompida, você receberá a melhor mensagem de erro possível, o mais rápido possível, em vez de simplesmente ignorar o impossível. E mesmo que a implementação Java não esteja quebrada, alguém poderia ter alterado seu código para "UTF8"(oops - deveria ter sido"UTF-8" ?).

Isso deveria ter sido uma exceção de tempo de execução em primeiro lugar. O JDK está cheio desse tipo de escolhas erradas.

Joonas Pulakka
fonte
4
A decisão realmente ruim (ou omissão, se você quiser) é que não há instâncias predefinidas do Charset para esses 6 conjuntos de caracteres exigidos pelo Java. foobar = new InputStreamReader(p.getInputStream(), Charset.ISO_8859_1);- agora isso não seria mais agradável e evitaria qualquer erro de uma vez para sempre?
user281377
3
A biblioteca da Guava tem toneladas dessas coisas. Facilita a vida.
3
O Java 1.7+ possui constantes de conjunto de caracteres definidas: docs.oracle.com/javase/7/docs/api/java/nio/charset/… . Use-os da seguinte maneira: StandardCharsets.UTF_8.displayName ()
Michael
4

Se você é o único desenvolvedor que verá esse código, eu diria que está bem, mas se você não for, eu o trataria como uma possibilidade real ou, pelo menos, mudaria o comentário "nunca vai acontecer" para algo mais útil.

Thanos Papathanasiou
fonte
4

A parte dessas exceções que mais me incomoda é que isso prejudica minha cobertura de código.

Quando estou ficando compulsivo com a cobertura, vou fazer uma tentativa / captura que "nunca pode acontecer" (... ou apenas se estiver usando uma JVM mutante que de alguma forma se esqueceu de incluir "US-ASCII") em uma classe e método que encapsula essa tentativa / captura e substitua a exceção verificada de uma das maneiras mencionadas aqui (geralmente lançando uma exceção não verificada com uma mensagem maliciosa).

Em seguida, minha cobertura de código é atingida na classe de utilitário, mas não em todas as referências a essa operação espalhadas pelo meu código.

Às vezes, aproveito o tempo para rolar como operações em uma classe que realmente possui semântica coerente. Mas como é óbvio para os meus colegas de equipe o que está acontecendo, eu geralmente mantenho o mais simples possível e não me preocupo muito com o melhor design possível.

No entanto, como um comentário mencionado, o Goiaba e outras bibliotecas têm maneiras de mitigar essa dor - mas essa é basicamente a mesma estratégia. Mova o aborrecimento para fora do palco para que seu código principal não seja afetado pela cobertura.

Roubar
fonte