When Should an Illegalargumentexception Be Thrown

When should an IllegalArgumentException be thrown?

Treat IllegalArgumentException as a preconditions check, and consider the design principle: A public method should both know and publicly document its own preconditions.

I would agree this example is correct:

void setPercentage(int pct) {
if( pct < 0 || pct > 100) {
throw new IllegalArgumentException("bad percent");
}
}

If EmailUtil is opaque, meaning there's some reason the preconditions cannot be described to the end-user, then a checked exception is correct. The second version, corrected for this design:

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

If EmailUtil is transparent, for instance maybe it's a private method owned by the class under question, IllegalArgumentException is correct if and only if its preconditions can be described in the function documentation. This is a correct version as well:

/** @param String email An email with an address in the form abc@xyz.com
* with no nested comments, periods or other nonsense.
*/
public String scanEmail(String email)
if (!addressIsProperlyFormatted(email)) {
throw new IllegalArgumentException("invalid address");
}
return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
// Assumes email is valid
boolean parsesJustFine = true;
// Parse logic
if (!parsesJustFine) {
// As a private method it is an internal error if address is improperly
// formatted. This is an internal error to the class implementation.
throw new AssertError("Internal error");
}
}

This design could go either way.

  • If preconditions are expensive to describe, or if the class is intended to be used by clients who don't know whether their emails are valid, then use ParseException. The top level method here is named scanEmail which hints the end user intends to send unstudied email through so this is likely correct.
  • If preconditions can be described in function documentation, and the class does not intent for invalid input and therefore programmer error is indicated, use IllegalArgumentException. Although not "checked" the "check" moves to the Javadoc documenting the function, which the client is expected to adhere to. IllegalArgumentException where the client can't tell their argument is illegal beforehand is wrong.

A note on IllegalStateException: This means "this object's internal state (private instance variables) is not able to perform this action." The end user cannot see private state so loosely speaking it takes precedence over IllegalArgumentException in the case where the client call has no way to know the object's state is inconsistent. I don't have a good explanation when it's preferred over checked exceptions, although things like initializing twice, or losing a database connection that isn't recovered, are examples.

How to use throws IllegalArgumentException in method declaration

You can do that simply at the beginning of the method:

public double getPrice(double d) throws IllegalArgumentException {
if(d <= 0) {
throw new IllegalArgumentException();
}

// rest of code
}

Also the throws IllegalArgumentException is not really needed in the declaration of the method. This must only be done with checked exceptions. But IllegalArgumentException belongs to the unchecked exceptions.

For more information about those I recommend reading this other question.

Should I put throws IllegalArgumentException at the function?

RuntimeExceptions like IllegalArgumentException are used to indicate programming errors. The program itself should rarely be able to handle it. Someone needs to manually fix the code.

Potential RuntimeExceptions should be documented somehow in the function contract (i.e. javadoc), either with the explicit @throws, or while describing the inputs. If you don't have a javadoc for the function, you may want to add the throws clause just to document the potential pitfalls of using the function, but in general adding throws clauses for runtime exceptions is discouraged.

If giving a wrong length is not actually a programming error, but is an exception situation, I would create a new checked exception (such as BadLengthError). If it is not an exceptional situation, don't use exceptions for flow control.

Correct usage of IllegalArgumentException

I think the first usage is correct:

if (!Files.isRegularFile(mazeFile) || !Files.isReadable(mazeFile)) {
throw new IllegalArgumentException("Cannot locate readable file "+mazeFile);
}

Since (as the documentation states) an invalid file was provided as the argument, this should throw an IllegalArgumentException.
Once you know you have an actual file that meets those requirements, I personally don't think that is a good exception to throw. This will cause other developers to question the type of argument that was given as opposed to the contents of the file. I guess your options are:

  • Keep it as is, just with very specific error messages explaining
    why this was an invalid argument.

  • Use some other, potentially more applicable java exception such as java.text.ParseException, since it is the file parsing that is causing the error.

  • Create a custom exception class that more sufficiently describes the issue with the file, e.g. a MazeParseException (per the comments) or a FileFormatException.

I would expect the second or third option to be more beneficial if you anticipate several other developers executing your function.

About throwing IllegalArgumentException

Throwing any exception makes the current method exit immediately, but not the whole program. Furthermore, the exception will continue being thrown at the calling method, from where the first method threw it; this is called propagation. Whether it keeps going at that point depends on whether that calling method catches the exception. If nothing catches the exception, it'll propagate all the way back to the main method (this is assuming a simple, single-threaded application), and if that method also throws it, then the program will exit.

void callee() {
if (someTrueCondition()) {
throw new IllegalArgumentException("whatever");
}
System.out.println("this will never get called");
}

void caller() {
calle(); // the IllegalArgumentException propagates from here
System.out.println("this will also never get called");
}

The above is true for any exception. What makes IllegalArgumentException different from others is only the fact that it's unchecked, and thus doesn't need to be declared in a throws clause on the method that may throw it. (Note that in your question, you said you probably would need to declare throws IllegalArgumentException, but you actually don't, because it's an unchecked exception.)

If you were instead throwing a checked exception (e.g., throw new SQLException()), then both callee and caller would need to declare that exception. callee would need to declare it because it throws that exception directly, and caller would need to declare it because it may throw that exception indirectly, via propagation from callee.

The only way to stop this propagation (both of the exception at runtime, and of the throws clauses at compile-time) is to catch the given exception with a try-catch.

Unable to Throw an IllegalArgumentException

You're almost there... in your if you're ensuring that all scores are within the proper range.

When the if fails, you want to throw the IllegalArgumentException in an else, like this:

if (scores[0] >= 0 && scores[0] <= 100 || 
scores[1] >= 0 && scores[1] <= 100 ||
scores[2] >= 0 && scores[2] <= 100)
{
average = totalScores / 3;
System.out.println("Average Score: " + average);
}
else
{
throw new IllegalArgumentException("Numbers were too low or high.");
}

How do I throw and catch an IllegalArgumentException?

To throw an exception, you use keyword throw. To catch, you use the try / catch construct. See this for more details:

  • http://docs.oracle.com/javase/tutorial/essential/exceptions/try.html

For your case, you'd do something like this - this is a test case:

try {
throw new IllegalArgumentException("Threw an IllegalArgumentException");
} catch(IllegalArgumentException e) {
System.out.println("Caught an IllegalArgumentException..." + e.getMessage());
}

You should, however, look into your code to see why IllegalArgumentException is being thrown anyway and fix that part. Using exceptions and try / catch is for unexpected events, not events that you expect to happen and that you can handle in a better way.

For example, FileNotFoundException gets thrown when a file could not be found. You generally try / catch that, so that you do something if the file was not found. However, if it's expected in a reasonable number of cases that the file might not be there, it would be better to first check if the file exists and then if it does actually do something with it.



Related Topics



Leave a reply



Submit