问题
I have a simple setter method for a property and null
is not appropriate for this particular property. I have always been torn in this situation: should I throw an IllegalArgumentException, or a NullPointerException? From the javadocs, both seem appropriate. Is there some kind of an understood standard? Or is this just one of those things that you should do whatever you prefer and both are really correct?
回答1:
It seems like an IllegalArgumentException
is called for if you don't want null
to be an allowed value, and the NullPointerException
would be thrown if you were trying to use a variable that turns out to be null
.
回答2:
You should be using IllegalArgumentException
(IAE), not NullPointerException
(NPE) for the following reasons:
First, the NPE JavaDoc explicitly lists the cases where NPE is appropriate. Notice that all of them are thrown by the runtime when null
is used inappropriately. In contrast, the IAE JavaDoc couldn't be more clear: "Thrown to indicate that a method has been passed an illegal or inappropriate argument." Yup, that's you!
Second, when you see an NPE in a stack trace, what do you assume? Probably that someone dereferenced a null
. When you see IAE, you assume the caller of the method at the top of the stack passed in an illegal value. Again, the latter assumption is true, the former is misleading.
Third, since IAE is clearly designed for validating parameters, you have to assume it as the default choice of exception, so why would you choose NPE instead? Certainly not for different behavior -- do you really expect calling code to catch NPE's separately from IAE and do something different as a result? Are you trying to communicate a more specific error message? But you can do that in the exception message text anyway, as you should for all other incorrect parameters.
Fourth, all other incorrect parameter data will be IAE, so why not be consistent? Why is it that an illegal null
is so special that it deserves a separate exception from all other types of illegal arguments?
Finally, I accept the argument given by other answers that parts of the Java API use NPE in this manner. However, the Java API is inconsistent with everything from exception types to naming conventions, so I think just blindly copying (your favorite part of) the Java API isn't a good enough argument to trump these other considerations.
回答3:
The standard is to throw the NullPointerException
. The generally infallible "Effective Java" discusses this briefly in Item 42 (first edition), Item 60 (second edition), or Item 72 (third edition) "Favor the use of standard exceptions":
"Arguably, all erroneous method invocations boil down to an illegal argument or illegal state, but other exceptions are standardly used for certain kinds of illegal arguments and states. If a caller passes null in some parameter for which null values are prohibited, convention dictates that NullPointerException be thrown rather than IllegalArgumentException."
回答4:
I was all in favour of throwing IllegalArgumentException
for null parameters, until today, when I noticed the java.util.Objects.requireNonNull
method in Java 7. With that method, instead of doing:
if (param == null) {
throw new IllegalArgumentException("param cannot be null.");
}
you can do:
Objects.requireNonNull(param);
and it will throw a NullPointerException
if the parameter you pass it is null
.
Given that that method is right bang in the middle of java.util
I take its existence to be a pretty strong indication that throwing NullPointerException
is "the Java way of doing things".
I think I'm decided at any rate.
Note that the arguments about hard debugging are bogus because you can of course provide a message to NullPointerException
saying what was null and why it shouldn't be null. Just like with IllegalArgumentException
.
One added advantage of NullPointerException
is that, in highly performance critical code, you could dispense with an explicit check for null (and a NullPointerException
with a friendly error message), and just rely on the NullPointerException
you'll get automatically when you call a method on the null parameter. Provided you call a method quickly (i.e. fail fast), then you have essentially the same effect, just not quite as user friendly for the developer. Most times it's probably better to check explicitly and throw with a useful message to indicate which parameter was null, but it's nice to have the option of changing that if performance dictates without breaking the published contract of the method/constructor.
回答5:
I tend to follow the design of JDK libraries, especially Collections and Concurrency (Joshua Bloch, Doug Lea, those guys know how to design solid APIs). Anyway, many APIs in the JDK pro-actively throws NullPointerException
.
For example, the Javadoc for Map.containsKey
states:
@throws NullPointerException if the key is null and this map does not permit null keys (optional).
It's perfectly valid to throw your own NPE. The convention is to include the parameter name which was null in the message of the exception.
The pattern goes:
public void someMethod(Object mustNotBeNull) {
if (mustNotBeNull == null) {
throw new NullPointerException("mustNotBeNull must not be null");
}
}
Whatever you do, don't allow a bad value to get set and throw an exception later when other code attempts to use it. That makes debugging a nightmare. You should always the follow the "fail-fast" principle.
回答6:
Voted up Jason Cohen's argument because it was well presented. Let me dismember it step by step. ;-)
The NPE JavaDoc explicitly says, "other illegal uses of the null object". If it was just limited to situations where the runtime encounters a null when it shouldn't, all such cases could be defined far more succinctly.
Can't help it if you assume the wrong thing, but assuming encapsulation is applied properly, you really shouldn't care or notice whether a null was dereferenced inappropriately vs. whether a method detected an inappropriate null and fired an exception off.
I'd choose NPE over IAE for multiple reasons
- It is more specific about the nature of the illegal operation
- Logic that mistakenly allows nulls tends to be very different from logic that mistakenly allows illegal values. For example, if I'm validating data entered by a user, if I get value that is unacceptable, the source of that error is with the end user of the application. If I get a null, that's programmer error.
- Invalid values can cause things like stack overflows, out of memory errors, parsing exceptions, etc. Indeed, most errors generally present, at some point, as an invalid value in some method call. For this reason I see IAE as actually the MOST GENERAL of all exceptions under RuntimeException.
Actually, other invalid arguments can result in all kinds of other exceptions. UnknownHostException, FileNotFoundException, a variety of syntax error exceptions, IndexOutOfBoundsException, authentication failures, etc., etc.
In general, I feel NPE is much maligned because traditionally has been associated with code that fails to follow the fail fast principle. That, plus the JDK's failure to populate NPE's with a message string really has created a strong negative sentiment that isn't well founded. Indeed, the difference between NPE and IAE from a runtime perspective is strictly the name. From that perspective, the more precise you are with the name, the more clarity you give to the caller.
回答7:
It's a "Holy War" style question. In others words, both alternatives are good, but people will have their preferences which they will defend to the death.
回答8:
If it's a setter
method and null
is being passed to it, I think it would make more sense to throw an IllegalArgumentException
. A NullPointerException
seems to make more sense in the case where you're attempting to actually use the null
.
So, if you're using it and it's null
, NullPointer
. If it's being passed in and it's null
, IllegalArgument
.
回答9:
Apache Commons Lang has a NullArgumentException that does a number of the things discussed here: it extends IllegalArgumentException and its sole constructor takes the name of the argument which should have been non-null.
While I feel that throwing something like a NullArgumentException or IllegalArgumentException more accurately describes the exceptional circumstances, my colleagues and I have chosen to defer to Bloch's advice on the subject.
回答10:
Couldn't agree more with what's being said. Fail early, fail fast. Pretty good Exception mantra.
The question about which Exception to throw is mostly a matter of personal taste. In my mind IllegalArgumentException seems more specific than using a NPE since it's telling me that the problem was with an argument I passed to the method and not with a value that may have been generated while performing the method.
My 2 Cents
回答11:
Actually, the question of throwing IllegalArgumentException or NullPointerException is in my humble view only a "holy war" for a minority with an incomlete understanding of exception handling in Java. In general, the rules are simple, and as follows:
- argument constraint violations must be indicated as fast as possible (-> fast fail), in order to avoid illegal states which are much harder to debug
- in case of an invalid null pointer for whatever reason, throw NullPointerException
- in case of an illegal array/collection index, throw ArrayIndexOutOfBounds
- in case of a negative array/collection size, throw NegativeArraySizeException
- in case of an illegal argument that is not covered by the above, and for which you don't have another more specific exception type, throw IllegalArgumentException as a wastebasket
- on the other hand, in case of a constraint violation WITHIN A FIELD that could not be avoided by fast fail for some valid reason, catch and rethrow as IllegalStateException or a more specific checked exception. Never let pass the original NullPointerException, ArrayIndexOutOfBounds, etc in this case!
There are at least three very good reasons against the case of mapping all kinds of argument constraint violations to IllegalArgumentException, with the third probably being so severe as to mark the practice bad style:
(1) A programmer cannot a safely assume that all cases of argument constraint violations result in IllegalArgumentException, because the large majority of standard classes use this exception rather as a wastebasket if there is no more specific kind of exception available. Trying to map all cases of argument constraint violations to IllegalArgumentException in your API only leads to programmer frustration using your classes, as the standard libraries mostly follow different rules that violate yours, and most of your API users will use them as well!
(2) Mapping the exceptions actually results in a different kind of anomaly, caused by single inheritance: All Java exceptions are classes, and therefore support single inheritance only. Therefore, there is no way to create an exception that is truly say both a NullPointerException and an IllegalArgumentException, as subclasses can only inherit from one or the other. Throwing an IllegalArgumentException in case of a null argument therefore makes it harder for API users to distinguish between problems whenever a program tries to programmatically correct the problem, for example by feeding default values into a call repeat!
(3) Mapping actually creates the danger of bug masking: In order to map argument constraint violations into IllegalArgumentException, you'll need to code an outer try-catch within every method that has any constrained arguments. However, simply catching RuntimeException in this catch block is out of the question, because that risks mapping documented RuntimeExceptions thrown by libery methods used within yours into IllegalArgumentException, even if they are no caused by argument constraint violations. So you need to be very specific, but even that effort doesn't protect you from the case that you accidentally map an undocumented runtime exception of another API (i.e. a bug) into an IllegalArgumentException of your API. Even the most careful mapping therefore risks masking programming errors of other library makers as argument constraint violations of your method's users, which is simply hillareous behavior!
With the standard practice on the other hand, the rules stay simple, and exception causes stay unmasked and specific. For the method caller, the rules are easy as well: - if you encounter a documented runtime exception of any kind because you passed an illegal value, either repeat the call with a default (for this specific exceptions are neccessary), or correct your code - if on the other hand you enccounter a runtime exception that is not documented to happen for a given set of arguments, file a bug report to the method's makers to ensure that either their code or their documentation is fixed.
回答12:
The accepted practice if to use the IllegalArgumentException( String message ) to declare a parameter to be invalid and give as much detail as possible... So to say that a parameters was found to be null while exception non-null, you would do something like this:
if( variable == null )
throw new IllegalArgumentException("The object 'variable' cannot be null");
You have virtually no reason to implicitly use the "NullPointerException". The NullPointerException is an exception thrown by the Java Virtual Machine when you try to execute code on null reference (Like toString()).
回答13:
Throwing an exception that's exclusive to null
arguments (whether NullPointerException
or a custom type) makes automated null
testing more reliable. This automated testing can be done with reflection and a set of default values, as in Guava's NullPointerTester. For example, NullPointerTester
would attempt to call the following method...
Foo(String string, List<?> list) {
checkArgument(string.length() > 0);
// missing null check for list!
this.string = string;
this.list = list;
}
...with two lists of arguments: "", null
and null, ImmutableList.of()
. It would test that each of these calls throws the expected NullPointerException
. For this implementation, passing a null
list does not produce NullPointerException
. It does, however, happen to produce an IllegalArgumentException
because NullPointerTester
happens to use a default string of ""
. If NullPointerTester
expects only NullPointerException
for null
values, it catches the bug. If it expects IllegalArgumentException
, it misses it.
回答14:
Some collections assume that null
is rejected using NullPointerException
rather than IllegalArgumentException
. For example, if you compare a set containing null
to a set that rejects null
, the first set will call containsAll
on the other and catch its NullPointerException
-- but not IllegalArgumentException
. (I'm looking at the implementation of AbstractSet.equals
.)
You could reasonably argue that using unchecked exceptions in this way is an antipattern, that comparing collections that contain null
to collections that can't contain null
is a likely bug that really should produce an exception, or that putting null
in a collection at all is a bad idea. Nevertheless, unless you're willing to say that equals
should throw an exception in such a case, you're stuck remembering that NullPointerException
is required in certain circumstances but not in others. ("IAE before NPE except after 'c'...")
回答15:
As a subjective question this should be closed, but as it's still open:
This is part of the internal policy used at my previous place of employment and it worked really well. This is all from memory so I can't remember the exact wording. It's worth noting that they did not use checked exceptions, but that is beyond the scope of the question. The unchecked exceptions they did use fell into 3 main categories.
NullPointerException: Do not throw intentionally. NPEs are to be thrown only by the VM when dereferencing a null reference. All possible effort is to be made to ensure that these are never thrown. @Nullable and @NotNull should be used in conjunction with code analysis tools to find these errors.
IllegalArgumentException: Thrown when an argument to a function does not conform to the public documentation, such that the error can be identified and described in terms of the arguments passed in. The OP's situation would fall into this category.
IllegalStateException: Thrown when a function is called and its arguments are either unexpected at the time they are passed or incompatible with the state of the object the method is a member of.
For example, there were two internal versions of the IndexOutOfBoundsException used in things that had a length. One a sub-class of IllegalStateException, used if the index was larger than the length. The other a subclass of IllegalArgumentException, used if the index was negative. This was because you could add more items to the object and the argument would be valid, while a negative number is never valid.
As I said, this system works really well, and it took someone to explain why the distinction is there: "Depending on the type of error it is quite straightforward for you to figure out what to do. Even if you can't actually figure out what went wrong you can figure out where to catch that error and create additional debugging information."
NullPointerException: Handle the Null case or put in an assertion so that the NPE is not thrown. If you put in an assertion is just one of the other two types. If possible, continue debugging as if the assertion was there in the first place.
IllegalArgumentException: you have something wrong at your call site. If the values being passed in are from another function, find out why you are receiving an incorrect value. If you are passing in one of your arguments propagate the error checks up the call stack until you find the function that is not returning what you expect.
IllegalStateException: You have not called your functions in the correct order. If you are using one of your arguments, check them and throw an IllegalArgumentException describing the issue. You can then propagate the cheeks up against the stack until you find the issue.
Anyway, his point was that you can only copy the IllegalArgumentAssertions up the stack. There is no way for you to propagate the IllegalStateExceptions or NullPointerExceptions up the stack because they had something to do with your function.
回答16:
In general, a developer should never throw a NullPointerException. This exception is thrown by the runtime when code attempts to dereference a variable who's value is null. Therefore, if your method wants to explicitly disallow null, as opposed to just happening to have a null value raise a NullPointerException, you should throw an IllegalArgumentException.
回答17:
I wanted to single out Null arguments from other illegal arguments, so I derived an exception from IAE named NullArgumentException. Without even needing to read the exception message, I know that a null argument was passed into a method and by reading the message, I find out which argument was null. I still catch the NullArgumentException with an IAE handler, but in my logs is where I can see the difference quickly.
回答18:
the dichotomy... Are they non-overlapping? Only non-overlapping parts of a whole can make a dichotomy. As i see it:
throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));
回答19:
NullPointerException
thrown when attempting to access an object with a reference variable whose current value is null
.
IllegalArgumentException
thrown when a method receives an argument formatted differently than the method expects.
回答20:
According to your scenario, IllegalArgumentException
is the best pick, because null
is not a valid value for your property.
回答21:
Ideally runtime exceptions should not be thrown. A checked exception(business exception) should be created for your scenario. Because if either of these exception is thrown and logged, it misguides the developer while going through the logs. Instead business exceptions do not create that panic and usually ignored while troubleshooting logs.
回答22:
The definitions from the links to the two exceptions above are IllegalArgumentException: Thrown to indicate that a method has been passed an illegal or inappropriate argument. NullPointerException: Thrown when an application attempts to use null in a case where an object is required.
The big difference here is the IllegalArgumentException is supposed to be used when checking that an argument to a method is valid. NullPointerException is supposed to be used whenever an object being "used" when it is null.
I hope that helps put the two in perspective.
回答23:
If it's a "setter", or somewhere I'm getting a member to use later, I tend to use IllegalArgumentException.
If it's something I'm going to use (dereference) right now in the method, I throw a NullPointerException proactively. I like this better than letting the runtime do it, because I can provide a helpful message (seems like the runtime could do this too, but that's a rant for another day).
If I'm overriding a method, I use whatever the overridden method uses.
回答24:
You should throw an IllegalArgumentException, as it will make it obvious to the programmer that he has done something invalid. Developers are so used to seeing NPE thrown by the VM, that any programmer would not immediately realize his error, and would start looking around randomly, or worse, blame your code for being 'buggy'.
回答25:
In this case, IllegalArgumentException conveys clear information to the user using your API that the " should not be null". As other forum users pointed out you could use NPE if you want to as long as you convey the right information to the user using your API.
GaryF and tweakt dropped "Effective Java" (which I swear by) references which recommends using NPE. And looking at how other good APIs are constructed is the best way to see how to construct your API.
Another good example is to look at the Spring APIs. For example, org.springframework.beans.BeanUtils.instantiateClass(Constructor ctor, Object[] args) has a Assert.notNull(ctor, "Constructor must not be null") line. org.springframework.util.Assert.notNull(Object object, String message) method checks to see if the argument (object) passed in is null and if it is it throws a new IllegalArgumentException(message) which is then caught in the org.springframework.beans.BeanUtils.instantiateClass(...) method.
回答26:
If you choose to throw a NPE and you are using the argument in your method, it might be redundant and expensive to explicitly check for a null. I think the VM already does that for you.
来源:https://stackoverflow.com/questions/3881/illegalargumentexception-or-nullpointerexception-for-a-null-parameter