Aprecio muito os novos recursos do Java 8 sobre interfaces lambdas e métodos padrão. No entanto, ainda me aborreço com exceções verificadas. Por exemplo, se eu apenas quiser listar todos os campos visíveis de um objeto, gostaria de simplesmente escrever isto:
Arrays.asList(p.getClass().getFields()).forEach(
f -> System.out.println(f.get(p))
);
No entanto, como o get
método pode lançar uma exceção verificada, o que não concorda com o Consumer
contrato de interface, devo capturar essa exceção e escrever o seguinte código:
Arrays.asList(p.getClass().getFields()).forEach(
f -> {
try {
System.out.println(f.get(p));
} catch (IllegalArgumentException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
);
No entanto, na maioria dos casos, eu só quero que a exceção seja lançada como um RuntimeException
e deixe o programa manipular, ou não, a exceção sem erros de compilação.
Portanto, gostaria de ter sua opinião sobre minha solução controversa para aborrecimento de exceções verificadas. Para isso, criei uma interface auxiliar ConsumerCheckException<T>
e uma função de utilitário rethrow
( atualizada de acordo com a sugestão do comentário de Doval ) da seguinte forma:
@FunctionalInterface
public interface ConsumerCheckException<T>{
void accept(T elem) throws Exception;
}
public class Wrappers {
public static <T> Consumer<T> rethrow(ConsumerCheckException<T> c) {
return elem -> {
try {
c.accept(elem);
} catch (Exception ex) {
/**
* within sneakyThrow() we cast to the parameterized type T.
* In this case that type is RuntimeException.
* At runtime, however, the generic types have been erased, so
* that there is no T type anymore to cast to, so the cast
* disappears.
*/
Wrappers.<RuntimeException>sneakyThrow(ex);
}
};
}
/**
* Reinier Zwitserloot who, as far as I know, had the first mention of this
* technique in 2009 on the java posse mailing list.
* http://www.mail-archive.com/[email protected]/msg05984.html
*/
public static <T extends Throwable> T sneakyThrow(Throwable t) {
throw (T) t;
}
}
E agora eu posso escrever:
Arrays.asList(p.getClass().getFields()).forEach(
rethrow(f -> System.out.println(f.get(p)))
);
Não tenho certeza de que esse seja o melhor idioma para contornar as exceções verificadas, mas, como expliquei, gostaria de ter uma maneira mais conveniente de obter meu primeiro exemplo sem lidar com as exceções verificadas, e essa é a maneira mais simples que encontrei para fazer isso.
fonte
sneakyThrow
dentro derethrow
para lançar a exceção verificada original em vez de agrupá-la em umRuntimeException
. Como alternativa, você pode usar a@SneakyThrows
anotação do Projeto Lombok que faz a mesma coisa.Consumer
s inforEach
podem ser executados de maneira paralela ao usarStream
s paralelos . Um lançamento jogável de dentro do consumidor será propagado para o encadeamento de chamada, que 1) não interromperá os outros consumidores em execução simultânea, o que pode ou não ser apropriado, e 2) se mais de um consumidor lançar algo, apenas um dos lançamentos será visto pelo thread de chamada.Respostas:
Vantagens, desvantagens e limitações de sua técnica:
Se o código de chamada for manipular a exceção verificada, você DEVE adicioná-lo à cláusula throws do método que contém o fluxo. O compilador não forçará você a adicioná-lo mais, por isso é mais fácil esquecê-lo. Por exemplo:
Se o código de chamada já manipular a exceção verificada, o compilador lembrará que você deve adicionar a cláusula throws à declaração do método que contém o fluxo (caso contrário, ele dirá: A exceção nunca é lançada no corpo da instrução try correspondente) .
De qualquer forma, você não poderá cercar o próprio fluxo para capturar a exceção verificada INSIDE o método que contém o fluxo (se você tentar, o compilador dirá: A exceção nunca é lançada no corpo da instrução try correspondente).
Se você está chamando um método que literalmente nunca pode lançar a exceção declarada, não deve incluir a cláusula throws. Por exemplo: new String (byteArr, "UTF-8") lança UnsupportedEncodingException, mas UTF-8 é garantido pela especificação Java para sempre estar presente. Aqui, a declaração de lances é um incômodo e qualquer solução para silenciá-la com um mínimo de clichê é bem-vinda.
Se você odeia exceções verificadas e acha que elas nunca devem ser adicionadas à linguagem Java (um número crescente de pessoas pensa dessa maneira, e eu NÃO SOU uma delas), apenas não adicione a exceção verificada aos lançamentos cláusula do método que contém o fluxo. A exceção verificada se comportará como uma exceção NÃO Verificada.
Se você estiver implementando uma interface estrita na qual não tem a opção de adicionar uma declaração de throws, e ainda assim lançar uma exceção for inteiramente apropriado, agrupar uma exceção apenas para obter o privilégio de lançá-la resulta em um stacktrace com exceções falsas que não contribua com informações sobre o que realmente deu errado. Um bom exemplo é Runnable.run (), que não lança nenhuma exceção verificada. Nesse caso, você pode decidir não adicionar a exceção verificada à cláusula throws do método que contém o fluxo.
De qualquer forma, se você decidir NÃO adicionar (ou esquecer de adicionar) a exceção verificada à cláusula throws do método que contém o fluxo, esteja ciente destas 2 consequências de lançar exceções CHECKED:
O código de chamada não poderá identificá-lo pelo nome (se você tentar, o compilador dirá: A exceção nunca é lançada no corpo da instrução try correspondente). Ele irá borbulhar e provavelmente será capturado no loop principal do programa por alguns "catch Exception" ou "catch Throwable", que podem ser o que você deseja.
Ele viola o princípio da menor surpresa: não será mais suficiente capturar o RuntimeException para garantir a captura de todas as exceções possíveis. Por esse motivo, acredito que isso não deve ser feito no código da estrutura, mas apenas no código comercial que você controla completamente.
Referências:
NOTA: Se você decidir usar esta técnica, poderá copiar a
LambdaExceptionUtil
classe auxiliar do StackOverflow: https://stackoverflow.com/questions/27644361/how-can-i-throw-checked-exceptions-from-inside-java-8 -streams . Fornece a implementação completa (Função, Consumidor, Fornecedor ...), com exemplos.fonte
Neste exemplo, ele pode realmente falhar? Não pense assim, mas talvez o seu caso seja especial. Se realmente "não pode falhar", e é apenas uma coisa irritante para o compilador, eu gosto de quebrar a exceção e lançar um
Error
com um comentário "não pode acontecer". Torna as coisas muito claras para manutenção. Caso contrário, eles se perguntarão "como isso pode acontecer" e "quem diabos lida com isso?"Isso em uma prática controversa para YMMV. Provavelmente vou receber alguns votos negativos.
fonte
clone
um tipo seladoFoo
que é conhecido por apoiá-lo e ele lançaCloneNotSupportedException
. Como isso poderia acontecer, a menos que o código fosse vinculado a algum outro tipo inesperado deFoo
? E se isso acontecer, algo pode ser confiável?An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.
(citado no Javadoc deError
) Esse é exatamente esse tipo de situação, portanto, longe de ser inadequado, jogar umError
aqui é a opção mais apropriada.outra versão deste código em que a exceção verificada está apenas atrasada:
a mágica é que se você ligar:
seu código será obrigado a capturar a exceção
fonte
sneakyThrow é legal! Eu sinto totalmente sua dor.
Se você tiver que ficar em Java ...
O Paguro possui interfaces funcionais que agrupam exceções verificadas, para que você não precise mais pensar nisso. Ele inclui coleções imutáveis e transformações funcionais ao longo das linhas dos transdutores Clojure (ou Java 8 Streams) que recebem as interfaces funcionais e agrupam as exceções verificadas.
Há também o VAVr e o The Eclipse Collections para experimentar.
Caso contrário, use Kotlin
O Kotlin é compatível bidirecionalmente com Java, não possui exceções verificadas, nem primitivas (bem, não é o que o programador precisa pensar), um sistema de tipos melhorado, assume imutabilidade, etc. estou convertendo todo o código Java que posso para o Kotlin. Qualquer coisa que eu costumava fazer em Java, agora prefiro fazer no Kotlin.
fonte
As exceções verificadas são muito úteis e seu manuseio depende do tipo de produto que você codifica como parte:
Geralmente, uma biblioteca não deve manipular exceções verificadas, mas declarar como lançada pela API pública. O caso raro em que eles poderiam ser agrupados no RuntimeExcepton comum é, por exemplo, criar dentro do código da biblioteca algum documento xml particular e analisá-lo, criar algum arquivo temporário e depois ler esse documento.
Para aplicativos de desktop, você deve iniciar o desenvolvimento do produto criando sua própria estrutura de tratamento de exceções. Usando sua própria estrutura, geralmente você agrupa uma exceção verificada em dois a quatro wrappers (suas subclasses personalizadas de RuntimeExcepion) e as manipula por um único Exception Handler.
Para servidores, geralmente seu código será executado dentro de alguma estrutura. Se você não usar nenhum servidor (vazio), crie sua própria estrutura semelhante (com base nos tempos de execução) descritos acima.
Para exercícios acadêmicos, você sempre pode envolvê-los diretamente no RuntimeExcepton. Alguém pode criar uma estrutura minúscula também.
fonte
Você pode dar uma olhada neste projeto ; Eu o criei especificamente por causa do problema de exceções verificadas e interfaces funcionais.
Com ele, você pode fazer isso em vez de escrever seu código personalizado:
Como todas as interfaces Throwing * estendem suas contrapartes não Throwing, você pode usá-las diretamente no fluxo:
Ou você pode apenas:
E outras coisas
fonte
fields.forEach((ThrowingConsumer<Field>) f -> out.println(f.get(o)))