Qual é o Throwable preferencial para usar em um construtor de classe utilitário privado?
-
29-08-2019 - |
Pergunta
Effective Java (Segunda Edição) , ponto 4, discute usando construtores privados para impor noninstantiability. Aqui está o exemplo de código a partir do livro:
public final class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}
No entanto, AssertionError
não parece ser a coisa certa a jogar. Nada está sendo "afirmou", que é como a API define o uso de AssertionError .
Existe uma Throwable
diferente que é normalmente nesta situação? Será que um geralmente apenas jogar uma Exception
geral com uma mensagem? Ou é comum para escrever um Exception
personalizado para isso?
É muito trivial, mas mais do que qualquer coisa que eu acho que eu sou apenas um curioso sobre isso a partir de uma perspectiva de estilo e padrões.
Solução
Há uma afirmação: "Eu estou afirmando que esse construtor nunca será chamado". Então, na verdade, AssertionError
é correto aqui.
Outras dicas
Gosto incluindo o comentário de Bloch:
// Suppress default constructor for noninstantiability
Ou melhor ainda colocá-lo no erro:
private UtilityClass()
{
throw new AssertionError("Suppress default constructor for noninstantiability");
}
UnsupportedOperationException sons como o melhor fit, embora uma exceção verificada seria ainda melhor, uma vez que pode avisar alguém erroneamente instanciar a classe em tempo de compilação.
E sobre IllegalAcessError ? :)
Não, não, não, com todo o respeito para Josh Bloch, nunca jogue um AssertionError
a menos que seja a partir de uma afirmação. Se você quer um AssertionError aqui, jogue-o com assert(false)
. Então, alguém lendo o código pode encontrá-lo mais tarde.
Mesmo melhor, definir a sua própria exceção, dizem CantInstantiateUtilityClass
. então você vai ter um código que diz
try {
// some stuff
} catch (CantInstantiateUtilityClass e) {
// react
}
para que o leitor do catcher sabe o aconteceu.
Atualizar
De vez em quando alguns Wanders maldito tolo por aqui e downvotes isso de novo, quase quatro anos após o fato. Então, deixe-me notar que o padrão ainda define AssertionError
como o resultado de uma afirmação que falhou, não como o que alguns novato pensa deve para ser jogado no lugar de um bem exceção informativo definido. Infelizmente, boa disciplina exceção é, talvez, a habilidade menos encorajado em programação Java.
Quando o código exige a inclusão do JUnit como uma dependência, como dentro do maven <scope>test</scope>
escopo de teste, em seguida, ir direto para o método Assertion.fail()
e beneficiar de uma melhoria significativa na clareza.
public final class UtilityClass {
private UtilityClass() {
fail("The UtilityClass methods should be accessed statically");
}
}
Quando fora do âmbito de teste, você pode usar algo como o seguinte, o que exigiria uma importação estática para uso como acima. import static pkg.Error.fail;
public class Error {
private static final Logger LOG = LoggerFactory.getLogger(Error.class);
public static void fail(final String message) {
LOG.error(message);
throw new AssertionError(message);
// or use your preferred exception
// e.g InstantiationException
}
}
Qual o seguinte uso.
public class UtilityClassTwo {
private UtilityClassTwo() {
Error.fail("The UtilityClass methods should be accessed statically");
}
}
Na sua forma mais idiomática, todas elas se resumem a isso:
public class UtilityClassThree {
private UtilityClassThree() {
assert false : "The UtilityClass methods should be accessed statically";
}
}
Um dos construído em exceções, UnsupportedOperationException pode ser jogado para indicam que 'a operação solicitada não é suportado'.
private Constructor() {
throw new UnsupportedOperationException(
"Do not instantiate this class, use statically.");
}
meio de declaração quebrado um que você já quebrou um caderno de encargos do seu código. Por isso é a coisa certa aqui.
No entanto, como eu suponho que você vai ser privada instanciar uma instância, ele irá também chamar o construtor e causar uma de erros a menos que tenha outro construtor?
Você pode criar sua própria classe Throwable
estender, por exemplo:.
class NoninstantiabilityError extends Throwable
Isto tem as seguintes vantagens:
- O nome indica o problema
- Porque se estende directamente
Throwable
é improvável que ele vai ser pego por acidente - Porque se estende directamente
Throwable
é verificado e chamando o respectivo construtor por acidente exigiria captura a exceção
Exemplo de uso:
public final class UtilityClass {
private UtilityClass() throws NoninstantiabilityError {
throw new NoninstantiabilityError();
}
...
}