Having just read the
NullPointerException-IllegalArgumentException-AssertionError thread, I
was just wondering if my use of AssertionError in one case was valid.
This is a very pared-down interface of the code:
import java.util.*;
public final class ClassPool {
public interface SourceHandler {
public boolean hasClass(String name);
}
private static List<SourceHandler> handlers =
new LinkedList<SourceHandler>();
static {
// add a few handlers to the list...
handlers.add(new SourceHandler() {
public boolean hasClass(String name) {
return true;
}});
// This is not really an anonymous inner class, but it is the
// best way for me to make it compilable
}
// The user can add handlers but can never remove them.
public static void addHandler(SourceHandler handler) {
handlers.add(0, handler);
}
// Edited for simplicity
public static void getClass(String name) {
for (SourceHandler handler : handlers) {
if (handler.hasClass(name))
return; // Use this handler as the class
}
// Since our last handler claims to handle everything, we should
// never get here.
throw new AssertionError();
}
}
Where I throw the assertion error explicitly, I want to put an `assert
false;' statement in (with a comment, of course), but then I would need
to insert a `return null;' at the end of the method, which would
invalidate the method contract which forbids null as a result but
assertions may not be on.
Is it more appropriate to put a RuntimeException or other type of error
instead of using an AssertionError here?

Signature
Beware of bugs in the above code; I have only proved it correct, not
tried it. -- Donald E. Knuth
Mark Thornton - 30 Dec 2007 21:44 GMT
> Having just read the
> NullPointerException-IllegalArgumentException-AssertionError thread, I
[quoted text clipped - 21 lines]
> Is it more appropriate to put a RuntimeException or other type of error
> instead of using an AssertionError here?
If it ever did happen you would be annoyed that the error message didn't
include the name for which no handler was found. So at least pass 'name'
to the constructor.
Mark Thornton
Lew - 30 Dec 2007 21:49 GMT
> public static void getClass(String name) {
> for (SourceHandler handler : handlers) {
[quoted text clipped - 15 lines]
> Is it more appropriate to put a RuntimeException or other type of error
> instead of using an AssertionError here?
I would use IllegalStateException. The problem is that this depends on data
being wrong, i.e., a run-time condition that caused 'handlers' not to be
initialized correctly. This is not an algorithmic failure but a state
failure. The program is in an illegal state, therefore IllegalStateException.
Client code will not be able to recover from an AssertionError. Is that what
you want, or would you prefer that the client can detect and log the
exception, perhaps by plugging in its own default handler when it hits this?
Don't you think it might surprise a sysadmin to see an AssertionError if
they've turned off asserts?

Signature
Lew
Daniel Pitts - 30 Dec 2007 22:34 GMT
>> public static void getClass(String name) {
>> for (SourceHandler handler : handlers) {
[quoted text clipped - 29 lines]
> Don't you think it might surprise a sysadmin to see an AssertionError if
> they've turned off asserts?
How about this case:
public String getSomethingRandom() {
final int value = random.nextInt(3);
switch (value) {
case 0:
return "Nothing here.";
case 1:
return "Got something!";
case 2:
return "Twice the fun?";
default:
throw new AssertionError("Something wrong!");
}
}

Signature
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Patricia Shanahan - 30 Dec 2007 23:09 GMT
>>> public static void getClass(String name) {
>>> for (SourceHandler handler : handlers) {
[quoted text clipped - 45 lines]
> }
> }
How about:
assert false : "Something wrong!";
in code that should never be reached. That way, disabling assertions
does prevent throws of AssertionError, and AssertionError does indicate
an assertion has failed.
Patricia
Daniel Pitts - 30 Dec 2007 23:29 GMT
>>>> public static void getClass(String name) {
>>>> for (SourceHandler handler : handlers) {
[quoted text clipped - 56 lines]
>
> Patricia
You'll get a compiler error if you use assert false; Left up as an
exercise for the reader.

Signature
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Eric Sosman - 31 Dec 2007 14:39 GMT
> [...]
> You'll get a compiler error if you use assert false; Left up as an
> exercise for the reader.
Okay, I've exercised until I got cramps in my thinking
muscle, but I got no compiler errors. Could you elucidate?

Signature
Eric Sosman
esosman@ieee-dot-org.invalid
Patricia Shanahan - 31 Dec 2007 14:54 GMT
>> [...]
>> You'll get a compiler error if you use assert false; Left up as an
>> exercise for the reader.
>
> Okay, I've exercised until I got cramps in my thinking
> muscle, but I got no compiler errors. Could you elucidate?
If you simply replaced the throw with an assertion, with no other
changes, there would be a fall-through path to the end of the method,
not permitted in a method that returns a result.
Patricia
Lew - 31 Dec 2007 15:37 GMT
>>> [...]
>>> You'll get a compiler error if you use assert false; Left up as an
[quoted text clipped - 6 lines]
> changes, there would be a fall-through path to the end of the method,
> not permitted in a method that returns a result.
That's why there's an exception toss after the 'assert' in the example I provided.

Signature
Lew
Lew - 31 Dec 2007 02:49 GMT
> How about this case:
>
[quoted text clipped - 11 lines]
> }
> }
This seems like a good case for assertions, except for that disabling asserts
doesn't disable explicit throws like this one. It's a little simplistic, but
it does illustrate an invariant.
Here's more of a realistic use case for assert: replace the literal '3' with
a variable coming from a private source, that should have been constrained to
less than APP_MAXRAND. Put the whole violated condition in the assert:
public String getSomethingRandom()
{
final int value = random.nextInt( somePrivateMethod() );
// The assert really belongs here, but it's more fun below.
// It belongs here because here is where the invariant should hold.
switch ( value )
{
case 0:
return "Nothing here.";
case 1:
return "Got something!";
case 2:
return "Twice the fun?";
default:
assert value >= 0 && value < APP_MAXRAND;
throw new IllegalStateException( "Invalid random value "+ value );
}
}

Signature
Lew