Reputation: 120456
Effective Java (Second Edition), Item 4, discusses using private constructors to enforce noninstantiability. Here's the code sample from the book:
public final class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}
However, AssertionError
doesn't seem like the right thing to throw. Nothing is being "asserted", which is how the API defines the use of AssertionError.
Is there a different Throwable
that's typically in this situation? Does one usually just throw a general Exception
with a message? Or is it common to write a custom Exception
for this?
It's pretty trivial, but more than anything I guess I'm just curious about it from a style and standards perspective.
Upvotes: 36
Views: 13133
Reputation: 112414
No no no, with all due respect to Josh Bloch, never throw an AssertionError
unless it's from an assertion. If you want an AssertionError here, throw it with assert(false)
. Then someone reading the code can find it later.
Even better, define your own exception, say CantInstantiateUtilityClass
. then you'll have code that says
try {
// some stuff
} catch (CantInstantiateUtilityClass e) {
// react
}
so that the reader of the catcher knows what happened.
Let me just note that the standard still defines AssertionError
as the result of a failed assertion, not as what some beginner thinks ought to be thrown in place of a well-defined informative exception. Sadly, good exception discipline is perhaps the least encouraged skill in Java programming.
Upvotes: 2
Reputation: 6934
You can create your own class extending Throwable
, e.g.:
class NoninstantiabilityError extends Throwable
This has the following advantages:
Throwable
it is unlikely that it will be caught by accidentThrowable
it is checked and calling the respective constructor by accident would require catching the exceptionUsage example:
public final class UtilityClass {
private UtilityClass() throws NoninstantiabilityError {
throw new NoninstantiabilityError();
}
...
}
Upvotes: 0
Reputation: 5585
When the code requires the inclusion of the JUnit as a dependency such as within the maven test scope <scope>test</scope>
, then go straight to Assertion.fail()
method and benefit from significant improvement in clarity.
public final class UtilityClass {
private UtilityClass() {
fail("The UtilityClass methods should be accessed statically");
}
}
When outside the test scope, you could use something like the following, which would require a static import to use like above. 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
}
}
Which the following usage.
public class UtilityClassTwo {
private UtilityClassTwo() {
Error.fail("The UtilityClass methods should be accessed statically");
}
}
In its most idiomatic form, they all boil down to this:
public class UtilityClassThree {
private UtilityClassThree() {
assert false : "The UtilityClass methods should be accessed statically";
}
}
One of the built in exceptions, UnsupportedOperationException can be thrown to indicate that 'the requested operation is not supported'.
private Constructor() {
throw new UnsupportedOperationException(
"Do not instantiate this class, use statically.");
}
Upvotes: 2
Reputation: 26758
I like including Bloch's comment:
// Suppress default constructor for noninstantiability
Or better yet putting it in the Error:
private UtilityClass()
{
throw new AssertionError("Suppress default constructor for noninstantiability");
}
Upvotes: 12
Reputation: 346526
UnsupportedOperationException sounds like the best fit, though a checked exception would be even better, since it might warn someone erroneously instantiating the class at compile time.
Upvotes: 7
Reputation: 57333
A broken assertion means that you've broken a contract specification of your code. So it's the right thing here.
However, as I assume you'll be privately instantiating an instance, it will also call the constructor and cause an error- unless you have another constructor?
Upvotes: 0
Reputation: 223183
There is an assertion: "I'm asserting that this constructor will never be called". So, indeed, AssertionError
is correct here.
Upvotes: 43