问题
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.
回答1:
There is an assertion: "I'm asserting that this constructor will never be called". So, indeed, AssertionError
is correct here.
回答2:
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");
}
回答3:
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.
回答4:
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.
Update
Every so often some damn fool wanders by here and downvotes this again, almost four years after the fact. So, 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.
回答5:
What about IllegalAcessError ? :)
回答6:
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.");
}
回答7:
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?
回答8:
You can create your own class extending Throwable
, e.g.:
class NoninstantiabilityError extends Throwable
This has the following advantages:
- The name indicates the problem
- Because it directly extends
Throwable
it is unlikely that it will be caught by accident - Because it directly extends
Throwable
it is checked and calling the respective constructor by accident would require catching the exception
Usage example:
public final class UtilityClass {
private UtilityClass() throws NoninstantiabilityError {
throw new NoninstantiabilityError();
}
...
}
来源:https://stackoverflow.com/questions/398953/what-is-the-preferred-throwable-to-use-in-a-private-utility-class-constructor