Therefore, you must deal with the exception in advance, and you can either convert the exception to a Run-time exception, or bypass it without handling it. But should this be the case, is there a hidden error?
Problem
Just look at one example and the problem is clear. Suppose you have a list of file objects that you want to sort by their standard paths in dictionary order. The so-called standard path refers to the parsing of aliases, symbolic links and/. /and the complete absolute path obtained after/./. The local method uses a comparer, as shown in Listing 1:
1. Listing 1. Compare two files by standard path
2.importjava.io.file;
3.importjava.io.ioexception;
4.importjava.util.arraylist;
5.importjava.util.collections;
6.importjava.util.comparator;
7.
8.publicclassfilecomparatorimplementscomparator<file>{
9.
10.publicintcompare (FILEF1,FILEF2) {
11.returnf1.getcanonicalpath (). CompareTo (F2.getcanonicalpath ());
12.}
13.
14.publicstaticvoidmain (String[]args) {
15.arraylist<file>files=newarraylist<file> ();
16.for (Stringarg:args) {
17.files.add (ARG) (newFile);
18.}
19.collections.sort (Files,newfilecomparator ());
20.for (filef:files) {
21.system.out.println (f);
22.}
23.}
24.
25.}
Unfortunately, the code cannot be compiled. The problem is that the Getcanonicalpath () method throws a IOException because it requires access to the file system. Typically, when you use checked exceptions, you can use one of the following two methods:
1. Wrap the error code in a try block and catch the thrown exception.
2. The declaration of the packing method (in this case compare ()) also throws IOException.
Usually, the choice of method depends on whether the exception can be handled reasonably when the exception is thrown. If you can, then use the Try-catch block. If not, then declaring the wrapper method itself throws an exception. Unfortunately, neither of these techniques works for this example. IOException cannot be handled reasonably in the Compare () method. Technically, it seems possible-that is, return 0, 1, or-1, as shown in Listing 2:
26. Listing 2. Returns a default value when throwing an exception
27.publicintcompare (FILEF1,FILEF2) {
28.try{
29.returnf1.getcanonicalpath (). CompareTo (F2.getcanonicalpath ());
30.}
31.catch (Ioexceptionex) {
32.return-1;
33.}
34.}
However, this violates the convention of the Compare () method because it is not a stable result. For the same object, the two calls may produce different results. If you use this comparer to sort, it means that the final list is not sorted correctly. So now try the 2nd option-the Declaration compare () throws IOException:
35.publicintcompare (FILEF1,FILEF2) throwsioexception{
36.returnf1.getcanonicalpath (). CompareTo (F2.getcanonicalpath ());
37.}
This also cannot be done by compiling. Because the checked exception is part of the method signature, you cannot increase the checked exception when overriding the method, just as you cannot change the return type. Then there's one more option left: Catch the exception in compare (), convert it to a Run-time exception, and throw the Run-time exception, as shown in Listing 3:
38. Listing 3. Converting a checked exception to a Run-time exception
39.publicintcompare (FILEF1,FILEF2) {
40.try{
41.returnf1.getcanonicalpath (). CompareTo (F2.getcanonicalpath ());
42.}
43.catch (Ioexceptionex) {
44.thrownewRuntimeException (ex);
45.}
46.}
Unfortunately, although this can be done by compiling, this method does not work, the reason is more subtle. The comparator interface defines a contract (see Resources). This contract does not allow the method to throw a Run-time exception (to prevent a bug in the calling code from violating the generic type security). The method of using this comparer is reasonably dependent on it to compare two files without throwing any exceptions. They are not ready to handle unexpected exceptions in compare ().
It is for this subtle reason that it is a bad idea to let the runtime exception become the external condition that the code is dealing with. This is just an escape from the problem and does not really deal with the problem. The undesirable consequences of not handling exceptions still exist, including destroying data and getting incorrect results.
This is a dilemma. You cannot really effectively handle exceptions within compare (), and you cannot handle exceptions outside of compare (). What's left to do with the exception-system.exit ()? The only correct way is to avoid this dilemma altogether. Fortunately, there are at least two ways to do this.
Split the problem in Split
The first approach is to divide the problem into two. The comparison itself does not cause an exception. The comparison is just a string. Converting a file to a string through a standard path can cause an exception. The problem is easier to handle if the action that might throw an exception is separated from the operation that does not throw an exception. That is, you first convert all file objects to strings, and then sort the strings by using the string comparer (or even the natural sort of java.lang.String), and then sort the original file list with the sorted list of strings. This approach is not straightforward, but the advantage is that IOException are thrown before the list is changed. If an exception occurs, it only appears in a pre-designed place and does not cause damage, and the calling code can specify how the exception is handled. This is demonstrated in Listing 4:
1. Listing 4. Read First, then sort
2.importjava.io.file;
3.importjava.io.ioexception;
4.importjava.util.arraylist;
5.importjava.util.collections;
6.importjava.util.hashmap;
7.
8.publicclassfilecomparator{
9.
10.privatestaticarraylist<string>getcanonicalpaths (Arraylist<file>files)
11.throwsioexception{
12.arraylist<string>paths=newarraylist<string> ();
13.for (filefile:files) Paths.add (File.getcanonicalpath ());
14.returnpaths;
15.}
16.
17.publicstaticvoidmain (String[]args) throwsioexception{
18.arraylist<file>files=newarraylist<file> ();
19.for (Stringarg:args) {
20.files.add (ARG) (newFile);
21.}
22.
23.arraylist<string>paths=getcanonicalpaths (files);
24.
25.//tomaintaintheoriginalmapping
26.hashmap<string,file>map=newhashmap<string,file> ();
27.inti=0;
28.for (stringpath:paths) {
29.map.put (Path,files.get (i));
30.i++;
31.}
32.
33.collections.sort (paths);
34.files.clear ();
35.for (stringpath:paths) {
36.files.add (Map.get (path));
37.}
38.}
39.
40.}
Listing 4 does not eliminate the possibility of an I/O error. This cannot be done because the code here is powerless to provide such functionality. However, this problem can be referred to a more appropriate place to deal with.
Avoid problems
The method mentioned earlier is a bit complicated, so I suggest another approach: do not use the built-in compare () function or Collections.sort (). It may be convenient to use such a function, but it is not appropriate for the current situation. Comparable and comparator are designed for certain, predictable comparison operations. Once I/O no longer conforms to this situation, it is likely that the commonly used algorithms and interfaces will become inapplicable. Even if it is barely available, it is extremely efficient, for example, to compare files by content rather than by standard paths. For each of the two files that are compared, each comparison requires reading the contents of the file-and possibly even the complete content. As a result, efficient algorithms want to minimize the number of reads, and may want to cache the results of each read-or, if the file is large, hashcode-each file instead of reading each file each time it is compared. Again, you'll want to first populate a list of comparison keys and then sort them instead of inline sorting. It is conceivable to define a separate, parallel Iocomparator interface that throws the necessary exceptions, as shown in Listing 5:
41. Listing 5. Independent Iocomparator Interface
42.importjava.io.ioexception;
43.publicinterfaceiocomparator<t>{
44.intcompare (To1,to2) throwsioexception;
45.
It then defines a separate, close utility tree based on this class, which makes the necessary operations on the temporary copy of the collection, allowing exceptions to be thrown without causing the data structure to be in a potentially compromised, intermediate state. For example, listing 6 provides a basic bubble sort:
47. Listing 6. Sorting files by bubbling algorithm
48.importjava.io.ioexception;
49.importjava.util.arraylist;
50.importjava.util.list;
51.
52.publicclassiosorter{
53.
54.publicstatic<t>voidsort (List<t>list,iocomparator<?supert>comparator)
55.throwsioexception{
56.list<t>temp=newarraylist<t> (List.size ());
57.temp.addall (list);
58.
59.bubblesort (Temp,comparator);
60.
61.//copybacktooriginallistnowthatnoexceptionshavebeenthrown
62.list.clear ();
63.list.addall (temp);
64.}
65.
66.//ofcourseyoucanreplacethiswithabetteralgorithmsuchasquicksort
67.privatestatic<t>voidbubblesort (List<t>list,iocomparator<?supert>comparator)
68.throwsioexception{
69.for (Inti=1;i<list.size (); i++) {
70.for (Intj=0;j<list.size ()-i;j++) {
71.if (Comparator.compare (List.get (j), List.get (j+1)) >0) {
72.swap (LIST,J);
73.}
74.}
75.}
76.}
77.
78.privatestatic<t>voidswap (LIST<T>LIST,INTJ) {
79.ttemp=list.get (j);
80.list.set (J,list.get (j+1));
81.list.set (j+1,temp);
82.}
83.
84.}
This is not the only way. In order to be clear, listing 6 intends to emulate an existing Collections.sort () method, but perhaps a more efficient approach is to return a new list instead of modifying the old list directly to prevent the damage from throwing an exception when the list is modified.
In the end, you actually acknowledge and begin to deal with possible I/O errors instead of escaping it, and you can even make more advanced bug fixes. For example, Iocomparator may not be stumped by an I/O error-because many I/O problems are temporary-can be retried several times, as shown in Listing 7:
1. Listing 7. If you don't succeed at first, try again (but don't try too many times)
2.importjava.io.file;
3.importjava.io.ioexception;
4.
5.publicclasscanonicalpathcomparatorimplementsiocomparator<file>{
6.
7. @Override
8.publicintcompare (FILEF1,FILEF2) throwsioexception{
9.for (inti=0;i<3;i++) {
10.try{
11.returnf1.getcanonicalpath (). CompareTo (F2.getcanonicalpath ());
12.}
13.catch (Ioexceptionex) {
14.continue;
15.}
16.}
17.//lastchance
18.returnf1.getcanonicalpath (). CompareTo (F2.getcanonicalpath ());
19.}
20.
21.}
This technique does not solve the general comparator problem, because you have to retry countless times to avoid throwing exceptions, and many I/O problems are not temporary.
Is checked unusual a bad idea?
If the java.io.IOException is a run-time exception, rather than a checked exception, is the problem improved? The answer is in the negative. If IOException extends runtimeexception rather than java.lang.Exception, it is easier to write bugs and incorrect code that ignores the truly possible I/O errors and unexpectedly fails at run time.
However, it is not easier to write the correct code that is prepared and capable of handling I/O errors. Yes, this approach is more complex than if there is no unexpected I/O error and there is no need to prepare for this. However, eliminating checked exceptions from the Java language does not help us achieve that ideal. I/O errors and other environmental problems are normal, and positive preparation is much better than ignoring them.
In summary, checked exceptions are not unreasonable as part of the method signature. When you find yourself wanting to throw a checked exception from a method that is not allowed-and thus suppress an exception that should not be suppressed-then go back and organize again to consider why you should overwrite that method at first. It is likely that you should have taken a completely different approach.