问题
If I've got the function below, with two choices
private MyObject findBlank() {
for (int i = 0; i < pieces.length; i++) {
if(pieces[i].isBlank()){
return pieces[i];
}
}
return null;
}
private MyObject findBlank() {
for (int i = 0; i < pieces.length; i++) {
if(pieces[i].isBlank()){
return pieces[i];
}
}
throw new NoSuchFieldError("No blank piece found!");
}
From this method I know that it should always return an object one of the 'pieces' always is isBlank() == true
, the return null at the end is just to please the compiler. Since this is the case and my code wouldn't work anyway if it returned null, is this the correct please to throw an exception?
My options are:
- return null and the app will get a NullPointerException in some edge case
- return null and wrap the use of the method with (myObject != null) checks
- throw an exception which will blow it up at runtime
I guess what I'm asking is, is this the correct place to throw an exception? i.e. there is nothing I can do about it if it gets into the situation. Is this classed as 'exceptional' or should I null check what my method returns (which makes my code look horrible). If I know it shouldn't return null then I should throw the exception right?
Also how would I choose what exception, or extend one and throw my own?
回答1:
About your options, ask yourself if
- Is it a good idea to have your program blow up at some point after this method returned an unexpected value (i.e.
null
)? - What exactly will be hidden if you mask out the
null
return value? - Is it a good idea to blow up immediately, just because there was a wrong value?
Personally I'd go for option 2 or 3, depending on whether I like the answer to question 2 or 3 better. Option 1 definitely is a bad idea, especially if it's not supposed to happen. If the program throws a NPE way after your function returned, you'll have a hard time figuring out where the null
came from. Especially if it happens months after you finished working on this particular function.
If you choose to throw an exception, you immediately see where something went wrong and you can directly go there to figure out why it went wrong. Returning null
and checking for it in the calling function could also work, but only if you don't fail silently, but actually do something to handle the problem properly.
回答2:
I guess what I'm asking is, is this the correct place to throw an exception?
If it were an exceptional situation, then yes. If the possibility of not finding anything that matches the criteria is expected then the situation is not exceptional and you should return null
.
回答3:
Yes, you should throw a RuntimeException to indicate an "exceptional" situation that should not have occurred. IllegalStateException probably fits the bill. Make sure to include a message with any information that will help you find the bug if it is ever thrown.
回答4:
May be it's a good idea to use Null Object pattern.
Provide an object as a surrogate for the lack of an object of a given type. The Null Object provides intelligent do nothing behavior, hiding the details from its collaborators
So in that case you won't have to use exceptions or return null. You can always return the intended return type object. The trick is when you have nothing to return , instead of returning null
or throwing an exception you can return the Null object
which is the same type as intended return type.
This documentation has some examples and descriptions. And has a similar case as you, solved by the design pattern.
public class CustomerFactory {
public static final String[] names = {"Rob", "Joe", "Julie"};
public static AbstractCustomer getCustomer(String name){
for (int i = 0; i < names.length; i++) {
if (names[i].equalsIgnoreCase(name)){
return new RealCustomer(name);
}
}
return new NullCustomer();
}
}
回答5:
Apart from most of the answers I would like to point out that if performance is your concern then, Exceptions are way slower than returning null
Take a look at this code:
class Main {
public static void main(String[] args) {
testException();
testNull();
}
public static void testException() {
long st = System.currentTimeMillis();
for(int i=0;i<10000000;i++) {
try{
exp();
} catch(Exception e) {
}
}
long et = System.currentTimeMillis();
System.out.println("Time taken with exceptions : "+(et-st));
}
public static void testNull() {
long st = System.currentTimeMillis();
for(int i=0;i<10000000;i++) {
returnNull();
}
long et = System.currentTimeMillis();
System.out.println("Time taken with null : "+(et-st));
}
public static void exp() throws Exception {
throw new Exception();
}
public static Object returnNull() {
return null;
}
}
The results on my machine are :
Time taken with exceptions : 7526
Time taken with exceptions : 5
If the throwing exception is a rare condition in your code and won't happen frequently then the time taken in both the situations is almost same.
You will have to make Performance vs Maintainability / Readability trade off.
Read more about it here
回答6:
Return null most of time will draw to the information lost from contract view, the consumer can not know what is the reason for the wrong response if get the null from producer.
Looking your first code, there is two situation the outer code get the NULLPointerException: 1. The pieces is null 2. The pieces did not have such element
So return null will mis-lead the outer code for further operation, it will draw potential problem.
And talk about difference between return nullObject (not null) and exception, the major difference is PROBABILITY, that means: 1. If the empty situation is on much more probability, it should return nullObject so that ALL outer code can/should handle them explicitly. 2. If the empty situation is less probability, why not throw the exception so that final call function can handle it directly.
回答7:
I believe the correct answer depends on the code that is calling the method. There are two cases:
The calling code is unsure whether the object exists, and is going to have code that will treat the case where it does not exist specially.
The calling code is confident that the object exists, and indeed if it did not exists there would be some other deep problem with the logic and all you can do in this situation is give up and report an error.
In practice, I often implement both with a naming convention to tell them apart:
private MyObject findBlankOrNull() {
for (int i = 0; i < pieces.length; i++) {
if(pieces[i].isBlank()){
return pieces[i];
}
}
return null;
}
private MyObject findBlankOrFail() throws Exception {
MyObject obj = findBlankOrNull();
if (obj != null) {
return obj;
}
throw new NoSuchFieldError("No blank piece found!");
}
Notice the OnFail
version can alway be constructed calling the other, and then throwing an exception instead of returning the null. Essentially what you are doing is inserting the Exception throw in place of the return null in order to guarantee that no null is returned, and so that you don't need to have any code to test for null at the calling site.
I did a blog post on this topic: Return Null or Exception? that covers it in a little more detail.
回答8:
should always return an object or return null
and
the app will get a NullPointerException
in some edge case
those two are contradictory.
If you are really sure that you alwasy have pieces[i].isBlank()
then throw IllegalStateException
Otherwise handle the case according your requirements.
回答9:
if you array should always have a valid value to return, you should raise an exception as a fallback. (2sd case in your example)
Eventually, you can declare your own kind (Class) of exception.
回答10:
I would suggest using Maybe
(also known as Option
) data type that I just talked about in another answer a while ago.
This data type is available in Functional Java.
Usage:
private Option<MyObject> findBlank() {
for (int i = 0; i < pieces.length; i++) {
if(pieces[i].isBlank()){
return Option.some(pieces[i]);
}
}
return Option.none();
}
Sidenote:
Your findBack
method can be generalized to a method that takes a predicate as an argument, and finds and returns the first element that satisfies it.
Unsurprisingly, Functional Java already has that as well.
Let's assume for a moment that pieces
is a fj.data.List. Then your method can be rewritten as:
private Option<MyObject> findBlank() {
return pieces.find(new F1<MyObject, Boolean>() {
public Boolean f(MyObject p) {
return p.isBlank();
}
});
}
Another sidenote:
Perhaps the above code looks pretty gnarly. IntelliJ IDEA's "closure folding" can be of some help here.
来源:https://stackoverflow.com/questions/10873485/throw-an-exception-or-return-null