Was ist die bevorzugte Throwable in einer privaten Utility-Klasse Konstruktor zu benutzen?
-
29-08-2019 - |
Frage
Effective Java (Second Edition) , Punkt 4, bespricht mit privaten Konstruktoren erzwingen noninstantiability. Hier ist der Code Probe aus dem Buch:
public final class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}
Allerdings AssertionError
scheint nicht, wie das Richtige zu werfen. Nichts ist „behauptet“ ist, das ist, wie die API die Verwendung von AssertionError .
Gibt es eine andere Throwable
die in dieser Situation normalerweise ist? Hat man in der Regel nur eine allgemeine Exception
mit einer Botschaft werfen? Oder ist es üblich, eine benutzerdefinierte Exception
für diese zu schreiben?
Es ist ziemlich trivial, aber mehr als alles, was ich denke, ich, wenn ich nur neugierig bin von einem Stil und Standards Perspektive.
Lösung
Es ist eine Behauptung: „Ich bin zu behaupten, dass dieser Konstruktor wird nie genannt werden“. Also, in der Tat, AssertionError
ist richtig hier.
Andere Tipps
Ich mag einschließlich Blochs Kommentar:
// Suppress default constructor for noninstantiability
Oder noch besser in den Fehlern setzen:
private UtilityClass()
{
throw new AssertionError("Suppress default constructor for noninstantiability");
}
UnsupportedOperationException klingt wie die beste fit, wenn eine geprüfte Ausnahme wäre noch besser, da es jemand fälschlicherweise instanziieren die Klasse bei der Kompilierung warnen könnte.
Was IllegalAcessError ? :)
Nein, nein, nein, mit Verlaub zu Josh Bloch, nie eine AssertionError
werfen, wenn es von einer Behauptung ist. Wenn Sie einen AssertionError hier wollen, werfen Sie es mit assert(false)
. Dann hat jemand den Code lesen kann es später finden.
Noch besser ist, definieren Sie Ihre eigene Ausnahme, sagt CantInstantiateUtilityClass
. dann werden Sie Code haben, der sagt
try {
// some stuff
} catch (CantInstantiateUtilityClass e) {
// react
}
, so dass der Leser des Fängers weiß was passiert ist.
Aktualisieren
Jeder so oft einige verdammter Narr wandert von hier und diese wieder downvotes, fast vier Jahre nach der Tat. Also, lassen Sie mich nur beachten Sie, dass die Standard- noch AssertionError
als Ergebnis einer verfehlten Behauptung definiert, nicht als das, was einige Anfänger denkt sollte anstelle eines gut geworfen werden definiert informative Ausnahme. Leider Disziplin gut Ausnahme ist vielleicht die am wenigsten ermutigt Geschick in Java-Programmierung.
Wenn der Code die Aufnahme des JUnit als eine Abhängigkeit wie im <scope>test</scope>
Maven Prüfumfang erfordert, dann geradeaus gehen Methode und profitieren von signifikanten Verbesserung der Klarheit Assertion.fail()
.
public final class UtilityClass {
private UtilityClass() {
fail("The UtilityClass methods should be accessed statically");
}
}
Wenn außerhalb des Testumfang, könnten Sie so etwas wie die folgenden verwenden, die einen statischen Import erfordern würden, wie oben zu verwenden. 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
}
}
Welche die folgende Nutzung.
public class UtilityClassTwo {
private UtilityClassTwo() {
Error.fail("The UtilityClass methods should be accessed statically");
}
}
In seiner idiomatischen Form, sie alle dazu einkochen:
public class UtilityClassThree {
private UtilityClassThree() {
assert false : "The UtilityClass methods should be accessed statically";
}
}
Einer der eingebauten Ausnahmen können UnsupportedOperationException zu werfen zeigen an, dass ‚die angeforderte Operation nicht unterstützt wird.‘
private Constructor() {
throw new UnsupportedOperationException(
"Do not instantiate this class, use statically.");
}
Eine gebrochene Behauptung bedeutet, dass Sie einen Vertrag Spezifikation des Codes gebrochen haben. So ist es das Richtige ist hier.
Aber, wie ich nehme an, Sie werden privat sein, eine Instanz instanziiert wird, wird es auch den Konstruktor aufrufen und eine fehler- verursachen, wenn Sie einen anderen Konstruktor haben?
Sie können Ihre eigene Klasse erweitern Throwable
, z erstellen.
class NoninstantiabilityError extends Throwable
Dies hat folgende Vorteile:
- Der Name zeigt an, das Problem
- Weil es
Throwable
direkt erstreckt es ist unwahrscheinlich, dass es durch Zufall gefangen werden - Da es sich direkt
Throwable
geprüft wird, und den jeweiligen Konstruktor durch Zufall Aufruf erfordern würde die Ausnahme abfangen
Anwendungsbeispiel:
public final class UtilityClass {
private UtilityClass() throws NoninstantiabilityError {
throw new NoninstantiabilityError();
}
...
}