问题
Typically Java methods look like:
public <U,V> U doSomething(V aReference) {
// Do something
}
This typically means that the method doSomething()
returns a null
if it
fails (for whatever reason) or a valid object reference. In some cases the
"valid object reference" may itself be null
. For example, the method
aMap.get(k)
may return null
if there is no key k
or if there is a key
k
but its corresponding value is null
. Confusion!
Not to mention NullPointerException
s if 50% of your LOC isn't just
null-checking.
What's wrong with methods looking like this:
public <T> ReturnTuple<T> doSomething(V aReference) {
T anotherObjRef = getValidObjT();
if (successful) {
return ReturnTuple.getSuccessTuple(anotherObjRef);
} else {
return ReturnTuple.getFailureTuple("aReference can't be null");
}
}
where the class ReturnTuple<T>
is defined something like:
class ReturnTuple<T> {
private boolean success;
// Read only if success == true
private T returnValue;
// Read only if success == false
private String failureReason;
// Private constructors, getters, setters & other convenience methods
public static <T> ReturnTuple<T> getSuccessTuple(T retVal) {
// This code is trivial
}
public static <T> ReturnTuple<T> getFailureTuple(String failureReason) {
// This code is trivial
}
}
Then the calling code will look like:
ReturnTuple<T> rt = doSomething(v);
if (rt.isSuccess()) {
// yay!
} else {
// boo hoo!
}
So, my question is: why isn't this pattern more common? What is wrong with it?
Keep in mind I am not asking for a critique of this exact code, but for a critique of this general idea.
Please note: the point here is not to get the code above to compile, just to discuss an idea. So please don't be too pedantic about code correctness :-).
Edit 1: Motivation
I guess I should have added this section from the beginning, but better late than never...
Ever wished a method could return two values at once? Or that the returning of a value could be de-linked from the ability to indicate success or failure?
This could also promote the idea of a method being a neat-and-clean self-contained unit (low coupling and high cohesion): handle all (or most) exceptions generated during it's execution (not talking about exceptions like
IllegalArgumentException
), discreetly log failure reasons (rather than the ugly stack trace of an uncaught exception) and only bother the caller with exactly the information required. IMHO this also promotes information-hiding and encapsulation.Done your best with testing, but when the code is deployed to the customer, an uncaught exception's ugly stack trace makes it all look so unprofessional.
Similar to the point above: you may have code that could possibly generate 20 different exceptions but you're catching only 5-7 of those. As we all know, customers do the damndest things: rely on them to cause all the other uncaught 13-15 exceptions :-). You end up looking bad when they see a big stack trace (instead of a discrete failure reason added to the logs).
This is the difference (for example) between showing a stack trace to a user in a web app vs. showing them a nicely formatted 5xx error page saying something like: "There was an error and your request couldn't be completed. Admins have been notified and will fix as soon as possible." etc.
This idea isn't entirely without merit as Java 8 provides the Optional class (as pointed out by @JBNizet) and Google's Guava library also has an Optional class. This just takes that a little further.
回答1:
This typically means that the method
doSomething()
returns a null if it fails
No, it does not mean that. It means that the method doSomething()
may sometimes legally return null
, without a failure. Java provides a powerful system for handling failures - namely, exception handling. This is how the API should indicate failures.
why isn't this [return a tuple] pattern more common? What is wrong with it?
The primary thing that is wrong with this pattern is that it is using a mechanism of reporting failures in a way that is foreign to Java. If your API runs into a failure, throw an exception. This saves you from creating twice as many objects as needed in "mainstream" cases, and keeps your APIs intuitively understandable to people who learned the Java class library well.
There are situations when returning a null
can be interpreted both ways - as a failure, and as a legitimate return value. Looking up objects in associative containers provide a good example: when you supply a key that is not in the map, one could claim that that is a programming error and throw an exception (.NET class library does that) or claim that when the key is missing, the corresponding spot in the map contains the default value, i.e. a null
- the way this is done in Java. In situations like that it is entirely acceptable to return a tuple. Java's Map
decided against this, most likely to save on creating additional objects every time an object is requested from a Map
.
来源:https://stackoverflow.com/questions/24585677/why-not-have-jave-methods-return-a-tuple-instead-of-an-object-reference-or-null