Qual é o uso pretendido de IllegalStateException?

Isso surgiu em uma discussão com um colega hoje.

Os Javadocs para o IllegalStateException do Java afirmam que:

Sinaliza que um método foi invocado em um horário ilegal ou inadequado. Em outras palavras, o ambiente Java ou o aplicativo Java não está em um estado apropriado para a operação solicitada.

E Java efetivo diz (Item 60, página 248):

Outra exceção comumente reutilizada é IllegalStateException. Esta é geralmente a exceção para lançar se a invocação for ilegal devido ao estado do object de recebimento. Por exemplo, essa seria a exceção para lançar se o chamador tentasse usar algum object antes de ter sido inicializado corretamente.

Parece que há um pouco de discrepância aqui. A segunda sentença dos javadocs faz parecer que a exceção poderia descrever uma condição muito ampla sobre o estado de execução do Java, mas a descrição em Effective Java faz parecer que é usada para condições relacionadas especificamente ao estado do estado do object cujo método foi chamado.

Os usos que eu vi no JDK (por exemplo, collections, Matcher ) e em Guava definitivamente parecem estar na categoria que o Effective Java fala sobre (“Este object está em um estado onde este método não pode ser chamado”). Isso também parece consistente com o IllegalArgumentException IllegalStateException .

Há algum uso legítimo de IllegalStateException no JDK relacionado ao “ambiente Java” ou “aplicativo Java”? Ou quaisquer guias de melhores práticas advogam usá-lo para o estado de execução mais amplo? Se não, por que diabos os javadocs são assim? 😉

Aqui está um uso particularmente legítimo desta exceção no JDK (veja: URLConnection.setIfModifiedSince(long) entre outros 300 usos:

 public void setIfModifiedSince(long ifmodifiedsince) { if (connected) throw new IllegalStateException("Already connected"); ifModifiedSince = ifmodifiedsince; } 

Eu acho que o exemplo é bem claro. Se o object estiver em estado particular (” Already connected “), algumas operações não devem ser chamadas. Nesse caso, quando a conexão foi estabelecida, algumas propriedades não podem ser definidas.

Essa exceção é especialmente útil quando sua class tem algum estado (máquina de estado?) Que muda com o tempo, tornando alguns methods irrelevantes ou impossíveis. Pense em uma class Car que tenha os methods start() , stop() e fuel() . Embora chamar start() duas vezes, uma após a outra, provavelmente não é nada errado, mas alimentar um carro inicial é certamente uma má ideia. Ou seja – o carro está em um estado errado.

Indiscutivelmente boa API não deve nos permitir chamar methods em estado errado para que problemas como esse sejam descobertos em tempo de compilation, não em tempo de execução. Neste exemplo específico, conectar-se a uma URL deve retornar um object diferente com um subconjunto de methods, todos os quais são válidos após a conexão.

Aqui está um exemplo no JDK. Existe uma class privada de pacote chamada java.lang.Shutdown. Se o sistema estiver sendo encerrado e você tentar adicionar um novo gancho, ele lançará a IllegalStateException. Pode-se argumentar que isso atende aos critérios da orientação “javadoc” – já que é o ambiente Java que está no estado errado.

 class Shutdown { ... /* Add a new shutdown hook. Checks the shutdown state and the hook itself, * but does not do any security checks. */ static void add(int slot, Runnable hook) { synchronized (lock) { if (state > RUNNING) throw new IllegalStateException("Shutdown in progress"); if (hooks[slot] != null) throw new InternalError("Shutdown hook at slot " + slot + " already registered"); hooks[slot] = hook; } } 

No entanto, também ilustra que realmente não há distinção entre a orientação “javadoc” e a orientação “Java eficaz”. Devido à maneira como o Shutdown é implementado, o encerramento da JVM é armazenado em um campo chamado state. Portanto, ele também atende à orientação “Java efetivo” para quando usar IllegalStateException, uma vez que o campo “state” faz parte do estado do object de recebimento. Como o object de recebimento (Shutdown) está no estado errado, ele lança a IllegalStateException.

Na minha opinião, as duas descrições de quando usar IllegalStateException são consistentes. A descrição efetiva do Java é um pouco mais prática, só isso. Para a maioria de nós, a parte mais importante de todo o ambiente Java é a class que estamos escrevendo agora, e é nisso que o autor está se concentrando.

Eu acho que se você ver o uso de IllegalStateException eu diria segundo se mais apropriado. Esta exceção é usada em muitos pacotes

  • java.net
  • java.nio
  • java.util
  • java.util.concurrrent etc

Para especificar um exemplo, ArrayBlockingQueue.add lança essa exceção se a fila já estiver cheia. Agora cheio é estado do object e está sendo invocado em hora imprópria ou ilegal

Eu acho que ambos significam o mesmo, mas a diferença de palavras.

Não há “discrepância” aqui. Não há nada no texto de Bloch que exclua o que diz no JLS. Bloch está simplesmente dizendo que se você tem a circunstância A, jogue essa exceção. Ele não está dizendo que essa exceção é / deveria ser lançada apenas nessa condição. O JLS está dizendo que esta exceção é lançada se A, B ou C.

Eu corri para isso com:

 try { MessageDigest digest = MessageDigest.getInstance("SHA-1"); ... } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } 

Eu acho que será impraticável para mim lançar IllegalStateException aqui no lugar de AssertionException mesmo que isso caia na categoria “o ambiente Java”.

Dada uma biblioteca, ela deve lançar uma IllegalStateException ou IllegalArgumentException sempre que detectar um bug devido ao código do usuário, enquanto a biblioteca deve lançar um AssertionError sempre que detectar um bug devido à própria implementação da biblioteca.

Por exemplo, nos testes da biblioteca, você pode esperar que a biblioteca IllegalStateException uma IllegalStateException quando a ordem das chamadas de método estiver errada. Mas você nunca esperará que a biblioteca lance um AssertionError .