The direct answer is because Comparator.compare
s interface is specified as such that it does not throw exceptions. But why is that?
Or to put it different: My Comparator
must depend on a function which can throw an exception. In theory, that should not happen. But if it happens, I want that it breaks out of the whole function where I am using that Comparator
(in Collections.sort
). I.e. I want that it just behaves as if an unhandled exception has occurred.
It seems that this is not possible in an obvious natural way (because if the interface says that it cannot throw an exception, it can'开发者_如何转开发t).
How would I solve this? With an ugly try/catch and print out the exception and hope that I recognize it? That seems to be a quite ugly way.
You can always throw a RuntimeException, or one derived from it, even from a method not explicitly declaring itself to throw exceptions.
In this case, I would rethrow an AssertionError
since you assume that the exception can't be raised. Don't forget to use the initCause()
method to propagate the information (AssertionError
doesn't have a constructor accepting a Throwable
)
This is contract of Comparator.compare method. If you want to use it you should just follow the rule not to throw checked exceptions from it :) Meanwhile, you can throw unchecked exception (RuntimeException or its subclass) and this will not break contract.
It's because you haven't annotated your method with Lombok's @SneakyThrows annotation.
Check it out at http://projectlombok.org/features/SneakyThrows.html
The demo and slides on the Lombok homepage are also worth a look http://projectlombok.org/
You ask different questions in the question title and in the question body.
You weren't clear on why the exception-capable function used by the compare()
method would throw an exception. It's either because there are certain uncomparable objects in the collection (like a NaN
numberic value) or else it's because there are certain pairs of objects that can't compared against each other.
Why can't I throw an exception in the Comparator?
I would guess Comparator.compare()
isn't designed to throw a checked exception because:
it is assumed that any items that you would wish to compare/sort would always be comparable.
if a
Comparator.compare()
could throw some sort of expected (i.e. checked) exception, then I can envision a couple of undesirable scenarios:a. a sort could abort because there is some sort of uncomparable object in there - the likely response being to remove the uncomparable object(s) and try the sort again
b. multiple sorts on the different orderings of the same collection of objects could sometimes abort with an exception and sometimes succeed depending on whether or not a pair uncomparable objects happened to come up for comparison during the sort
This is of course just my conjecture.
How would I solve this?
I going to assume that the reason the exception-possible function that your Comparator.compare()
uses throws an exception is because there's an uncomparable object in the collection (like a NaN
numberic value). Options include:
Sort a copy of the list with the uncomparable object(s) removed.
Throw an unchecked (runtime) exception to abort the sort. Not sure what you would do then other than #1 above.
Follow the
NaN
approach and make it so those objects come out at beginning or end.NaN
values are normally uncomparable to other values, but during a sort, the comparator defines its own total ordering so thatNaN
values end up at the end of the sorted collection.http://download.oracle.com/javase/1.4.2/docs/api/java/util/Arrays.html#sort(double[])
... The < relation does not provide a total order on all floating-point values; ... a NaN value compares neither less than, greater than, nor equal to any floating-point value, even itself.
... To allow the sort to proceed, ... this method uses the total order imposed by Double.compareTo(java.lang.Double).
... This ordering differs from the < relation in that ... NaN is considered greater than any other floating-point value. For the purposes of sorting, all NaN values are considered equivalent and equal.
To do this, code your
Comparator.compare()
such that any incomparable object always compares greater than any comparable object and it always compares equal to any other incomparable object.
There are 2 ways of solving this:
- Catching the exception and throwing it inside a
java.lang.RuntimeException()
, or - Catching the exception and logging it using Log4J or SLF4J (or any logger factory you feel comfortable with).
The contract for comparator's compare method doesn't throw exceptions.
You can rethrow a checked exception and avoid compilation error by using a number of tricks. The simplest is;
try {
// something
} catch (Exception e) {
Thread.currentThread().stop(e);
}
However, since the compiler has no idea you have done this. You can confuse it and yourself if you are not careful. One of the objectives of closures is to handle checked exceptions in things like Comparators correctly (while others would prefer they went away)
This article describes why throwing a RuntimeException from inside the Comparator interface is a bad idea, and it shows sample source code for two good-practice ways to handle checked exceptions from within interfaces that don't support them: 1) split the problem into two pieces, or 2) use your own comparator that supports checked exceptions.
https://www.ibm.com/developerworks/library/j-ce/index.html
The Comparator interface defines a contract. That contract does not allow this method to throw a run-time exception (barring violations of generic type safety that qualify as bugs in the calling code). Methods that use this comparator legitimately depend on it to compare two files, without throwing any exceptions. They will not be prepared to handle an exception that unexpectedly bubbles up from compare().
精彩评论