"The major difference between a thing that might go wrong and a thing that cannot possibly go wrong is that when a thing that cannot possibly go wrong goes wrong it usually turns out to be impossible to get at or repair." -Douglas Adams
“可能出错的事情与不可能出错的事情之间的主要区别在于,当一件不可能出错的事情出错时,通常会发现无法进入或修复。” -道格拉斯·亚当斯
I have an class FileItems. FileItems constructor takes a file, and throws an exception (FileNotFoundException) if the file doesn't exist. Other methods of that class also involve file operations and thus have the ability throw the FileNotFoundException. I would like to find a better solution. A solution which doesn't require that other programmers handle all of these extremely unlikely FileNotFoundExceptions.
我有一个类FileItems。 FileItems构造函数接受一个文件,如果该文件不存在则抛出异常(FileNotFoundException)。该类的其他方法也涉及文件操作,因此具有抛出FileNotFoundException的能力。我想找到一个更好的解决方案。一种解决方案,不需要其他程序员处理所有这些极不可能的FileNotFoundExceptions。
The facts of the matter:
事情的事实:
- The file has been checked to exist but the extremely unlikely possibility exists that through some major fault of reality the file might be deleted before this method is called.
- Since the probability of 1 happening is extremely unlike and unrecoverable, I would prefer to define an unchecked exception.
- The file is has already been found to exist, forcing other programmers to write code and catch the checked FileNotFoundException seems tedious and useless. The program should just fail completely at that point. For example there is always the chance that a computer may catch fire, but no one is insane enough to force other programmers to handle that as a checked exception.
- I run into this sort of Exception issue from time to time, and defining custom unchecked exceptions each time I encounter this problem (my old solution) is tiresome and adds to code-bloat.
该文件已被检查存在,但存在极不可能的可能性,通过一些重大的现实错误,在调用此方法之前可能会删除该文件。
由于1发生的概率非常不同且不可恢复,我宁愿定义一个未经检查的异常。
该文件已经被发现存在,迫使其他程序员编写代码并捕获已检查的FileNotFoundException,这似乎很乏味且无用。该程序应该在那时完全失败。例如,计算机总是有可能着火,但没有人疯狂到足以迫使其他程序员将其作为检查异常来处理。
我不时遇到这种异常问题,并且每次遇到这个问题时定义自定义未经检查的异常(我的旧解决方案)都很烦人并且增加了代码膨胀。
The code currently looks like this
代码目前看起来像这样
public Iterator getFileItemsIterator() {
try{
Scanner sc = new Scanner(this.fileWhichIsKnowToExist);
return new specialFileItemsIterator(sc);
} catch (FileNotFoundException e){ //can never happen}
return null;
}
How can I do this better, without defining a custom unchecked FileNotFoundException? Is there some way to cast a checkedException to an uncheckException?
如何在不定义自定义未经检查的FileNotFoundException的情况下更好地完成此操作?有没有办法将checkedException转换为uncheckException?
8 个解决方案
#1
The usual pattern to deal with this is exception chaining. You just wrap the FileNotFoundException in a RuntimeException:
解决这个问题的通常模式是异常链接。您只需在RuntimeException中包装FileNotFoundException:
catch(FileNotFoundException e) {
throw new RuntimeException(e);
}
This pattern is not only applicable when an Exception cannot occur in the specific situation (such as yours), but also when you have no means or intention to really handle the exception (such as a database link failure).
此模式不仅适用于在特定情况下(例如您的)不能发生异常,而且当您无法或无意真正处理异常(例如数据库链接失败)时。
Edit: Beware of this similar-looking anti-pattern, which I have seen in the wild far too often:
编辑:要注意这种看似相似的反模式,我经常在野外看到它:
catch(FileNotFoundException e) {
throw new RuntimeException(e.getMessage());
}
By doing this, you throw away all the important information in the original stacktrace, which will often make problems difficult to track down.
通过这样做,您可以丢弃原始堆栈跟踪中的所有重要信息,这通常会使问题难以追踪。
Another edit: As Thorbjørn Ravn Andersen correctly points out in his response, it doesn't hurt to state why you're chaining the exception, either in a comment or, even better, as the exception message:
另一个编辑:正如ThorbjørnRavnAndersen在他的回答中正确指出的那样,无论是在评论中,还是在更好的情况下,作为异常消息,都可以说明为什么要链接异常。
catch(FileNotFoundException e) {
throw new RuntimeException(
"This should never happen, I know this file exists", e);
}
#2
You can convert a checked exception to an unchecked by nestling it inside a RuntimException. If the exception is caught higher up in the stack and outputted using printStackTrace(), the stack for the original exception will be displayed as well.
您可以通过将其嵌入RuntimException中将已检查的异常转换为未选中的异常。如果异常在堆栈中被捕获并使用printStackTrace()输出,则也会显示原始异常的堆栈。
try {
// code...
}
catch (IOException e) {
throw new RuntimeException(e);
}
This is a good solution, that you shouldn't hesitate to use in these situations.
这是一个很好的解决方案,您应该毫不犹豫地在这些情况下使用。
#3
Consider using the form
考虑使用表单
throw new RuntimeException("This should never happen", e);
instead. This allows you to convey meaning to the maintainer to follow you, both when reading the code but also SHOULD the exception happen to be thrown in some strange scenario.
代替。这允许您向维护者传达意义,以便在阅读代码时跟随您,但也应该在某些奇怪的情况下抛出异常。
EDIT: This is also a good way to pass exceptions through a mechanism not expecting those exceptions. E.g. if you have a "get more rows from the database" iterator the Iterator interface does not allow to throw e.g. an FileNotFoundException so you can wrap it like this. In the code USING the iterator, you can then catch the runtimeexception and inspect the original excpetion with getCause(). VERY useful when going through legacy code paths.
编辑:这也是通过不期望这些异常的机制传递异常的好方法。例如。如果你有一个“从数据库中获取更多行”迭代器,Iterator接口不允许抛出例如一个FileNotFoundException,所以你可以像这样包装它。在使用迭代器的代码中,您可以捕获runtimeexception并使用getCause()检查原始的excpetion。在浏览遗留代码路径时非常有用。
#4
If your position is that this is so unlikely and should just end the program, use an existing runtime exception, even RuntimeException itself (if not IllegalStateException).
如果您的位置不太可能并且应该只是结束程序,请使用现有的运行时异常,甚至RuntimeException本身(如果不是IllegalStateException)。
try {
....
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
#5
You are probably better off throwing the superclass and more generic exception IOException
at any point in your code which involves reading or writing from the file.
你可能最好在代码中的任何一点抛出超类和更通用的异常IOException,这涉及从文件中读取或写入。
The file may exist when your class's constructor runs, but that doesn't guarantee that:
当您的类的构造函数运行时,该文件可能存在,但这并不能保证:
- It exists when methods are called
- It's writable/readable
- Another thread doesn't access it and somehow screw up your streams
- The resource doesn't go away in the middle of processing
它在调用方法时存在
另一个线程无法访问它,并以某种方式搞砸了你的流
资源不会在处理过程中消失
etc.
Instead of reinventing the wheel, I would say just re-throw IOException wherever the JDK/java.io
classes you are using force you to do so.
我会说重新抛出IOException而不是重新发明*,只要你使用的JDK / java.io类强制你这样做。
Also I for one hate classes that throw Exceptions from their constructor - I'd get rid of these if I were you.
我也是一个仇恨类,它们从构造函数中抛出异常 - 如果我是你,我会摆脱这些。
#6
I did a little googling and found this glob of code. It's a bit more flexible of an approach me thinks
我做了一点谷歌搜索,发现了这个代码。我认为这种方法更灵活一些
Compliments of this article
赞美这篇文章
class SomeOtherException extends Exception {}
public class TurnOffChecking {
private static Test monitor = new Test();
public static void main(String[] args) {
WrapCheckedException wce = new WrapCheckedException();
// You can call f() without a try block, and let
// RuntimeExceptions go out of the method:
wce.throwRuntimeException(3);
// Or you can choose to catch exceptions:
for(int i = 0; i < 4; i++)
try {
if(i < 3)
wce.throwRuntimeException(i);
else
throw new SomeOtherException();
} catch(SomeOtherException e) {
System.out.println("SomeOtherException: " + e);
} catch(RuntimeException re) {
try {
throw re.getCause();
} catch(FileNotFoundException e) {
System.out.println(
"FileNotFoundException: " + e);
} catch(IOException e) {
System.out.println("IOException: " + e);
} catch(Throwable e) {
System.out.println("Throwable: " + e);
}
}
monitor.expect(new String[] {
"FileNotFoundException: " +
"java.io.FileNotFoundException",
"IOException: java.io.IOException",
"Throwable: java.lang.RuntimeException: Where am I?",
"SomeOtherException: SomeOtherException"
});
}
} ///:~
#7
I have experienced the same problem.
我遇到了同样的问题。
In the simplest case you inform the user of the error and suggest to either to repeat or to cancel the operation.
在最简单的情况下,您通知用户错误并建议重复或取消操作。
In general case, workflow is a sequence of actions (I/O including), where each action "assumes" that the previous one had succeeded.
在一般情况下,工作流是一系列动作(包括I / O),其中每个动作“假定”前一个动作成功。
The approach I have chosen is to create a list of 'roll-back' actions. If the workflow succeeds, they are ignored. If an exception occurs, I execute rollbacks and present an exception to the user.
我选择的方法是创建一个“回滚”动作列表。如果工作流程成功,则会忽略它们。如果发生异常,我会执行回滚并向用户显示异常。
This:
- keeps integrity of data
- allows to code much easier
保持数据的完整性
允许编码更容易
Typical function is like:
典型功能如下:
returntype func(blah-blah-blah, Transaction tr)
{
// IO example
Stream s = null;
try
{
s = new FileStream(filename);
tr.AddRollback(File.Delete(filename));
}
finally
{
if (s != null)
s.close();
}
}
Typical usage is:
典型用法是:
Transaction tr = new Transaction();
try
{
DoAction1(blah1, tr);
DoAction2(blah2, tr);
//...
}
catch (Exception ex)
{
tr.ExecuteRollbacks();
// queue the exception message to the user along with a command to repeat all the actions above
}
This is a little bit trickier in real-world, because
这在现实世界中有点棘手,因为
- sometimes it is necessary to obtain a bunch of locks before execution actions
- rollback code should be exception-silent itself (for instance)
有时需要在执行操作之前获取一堆锁
回滚代码应该是异常 - 静默本身(例如)
But I have already get used to this approach, now my applications are more stable.
但我已经习惯了这种方法,现在我的应用程序更加稳定。
#8
Don't just tank your entire application with a RuntimeException
when you encounter an unlikely error condition. RuntimeException
should be reserved for programmer errors, and IOException
is most likely not caused by programmer error.
当遇到不太可能出现的错误情况时,不要只使用RuntimeException对整个应用程序进行处理。应该为程序员错误保留RuntimeException,并且IOException很可能不是由程序员错误引起的。
Instead, encapsulate the lower level exception in a higher level exception and rethrow. Then handle the higher level exception up the call chain.
相反,将较低级别的异常封装在更高级别的异常中并重新抛出。然后在调用链上处理更高级别的异常。
For example:
class SomeClass {
public void doActions() {
try {
someAction();
} catch (HigherLevelException e) {
notifyUser();
}
someOtherUnrelatedAction();
}
public void someAction() throws HigherLevelException {
try {
// user lower-level abstraction to do our bidding
} catch(LowerLevelException e) {
throw new HigherLevelException(e);
}
}
public void someOtherUnrelatedAction() {
// does stuff
}
}
Most likely the call stack that threw the exception was performing some task in your application. Instead of force crashing your entire application with a RuntimeException
figure out what to do when the problem occurs during that task. For example, were you trying to save a file? Don't crash, instead notify the user there was an issue.
抛出异常的调用堆栈很可能是在您的应用程序中执行某些任务。而不是使用RuntimeException强制崩溃整个应用程序,而不是在该任务期间发生问题时要做什么。例如,您是否尝试保存文件?不要崩溃,而是通知用户存在问题。
#1
The usual pattern to deal with this is exception chaining. You just wrap the FileNotFoundException in a RuntimeException:
解决这个问题的通常模式是异常链接。您只需在RuntimeException中包装FileNotFoundException:
catch(FileNotFoundException e) {
throw new RuntimeException(e);
}
This pattern is not only applicable when an Exception cannot occur in the specific situation (such as yours), but also when you have no means or intention to really handle the exception (such as a database link failure).
此模式不仅适用于在特定情况下(例如您的)不能发生异常,而且当您无法或无意真正处理异常(例如数据库链接失败)时。
Edit: Beware of this similar-looking anti-pattern, which I have seen in the wild far too often:
编辑:要注意这种看似相似的反模式,我经常在野外看到它:
catch(FileNotFoundException e) {
throw new RuntimeException(e.getMessage());
}
By doing this, you throw away all the important information in the original stacktrace, which will often make problems difficult to track down.
通过这样做,您可以丢弃原始堆栈跟踪中的所有重要信息,这通常会使问题难以追踪。
Another edit: As Thorbjørn Ravn Andersen correctly points out in his response, it doesn't hurt to state why you're chaining the exception, either in a comment or, even better, as the exception message:
另一个编辑:正如ThorbjørnRavnAndersen在他的回答中正确指出的那样,无论是在评论中,还是在更好的情况下,作为异常消息,都可以说明为什么要链接异常。
catch(FileNotFoundException e) {
throw new RuntimeException(
"This should never happen, I know this file exists", e);
}
#2
You can convert a checked exception to an unchecked by nestling it inside a RuntimException. If the exception is caught higher up in the stack and outputted using printStackTrace(), the stack for the original exception will be displayed as well.
您可以通过将其嵌入RuntimException中将已检查的异常转换为未选中的异常。如果异常在堆栈中被捕获并使用printStackTrace()输出,则也会显示原始异常的堆栈。
try {
// code...
}
catch (IOException e) {
throw new RuntimeException(e);
}
This is a good solution, that you shouldn't hesitate to use in these situations.
这是一个很好的解决方案,您应该毫不犹豫地在这些情况下使用。
#3
Consider using the form
考虑使用表单
throw new RuntimeException("This should never happen", e);
instead. This allows you to convey meaning to the maintainer to follow you, both when reading the code but also SHOULD the exception happen to be thrown in some strange scenario.
代替。这允许您向维护者传达意义,以便在阅读代码时跟随您,但也应该在某些奇怪的情况下抛出异常。
EDIT: This is also a good way to pass exceptions through a mechanism not expecting those exceptions. E.g. if you have a "get more rows from the database" iterator the Iterator interface does not allow to throw e.g. an FileNotFoundException so you can wrap it like this. In the code USING the iterator, you can then catch the runtimeexception and inspect the original excpetion with getCause(). VERY useful when going through legacy code paths.
编辑:这也是通过不期望这些异常的机制传递异常的好方法。例如。如果你有一个“从数据库中获取更多行”迭代器,Iterator接口不允许抛出例如一个FileNotFoundException,所以你可以像这样包装它。在使用迭代器的代码中,您可以捕获runtimeexception并使用getCause()检查原始的excpetion。在浏览遗留代码路径时非常有用。
#4
If your position is that this is so unlikely and should just end the program, use an existing runtime exception, even RuntimeException itself (if not IllegalStateException).
如果您的位置不太可能并且应该只是结束程序,请使用现有的运行时异常,甚至RuntimeException本身(如果不是IllegalStateException)。
try {
....
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
#5
You are probably better off throwing the superclass and more generic exception IOException
at any point in your code which involves reading or writing from the file.
你可能最好在代码中的任何一点抛出超类和更通用的异常IOException,这涉及从文件中读取或写入。
The file may exist when your class's constructor runs, but that doesn't guarantee that:
当您的类的构造函数运行时,该文件可能存在,但这并不能保证:
- It exists when methods are called
- It's writable/readable
- Another thread doesn't access it and somehow screw up your streams
- The resource doesn't go away in the middle of processing
它在调用方法时存在
另一个线程无法访问它,并以某种方式搞砸了你的流
资源不会在处理过程中消失
etc.
Instead of reinventing the wheel, I would say just re-throw IOException wherever the JDK/java.io
classes you are using force you to do so.
我会说重新抛出IOException而不是重新发明*,只要你使用的JDK / java.io类强制你这样做。
Also I for one hate classes that throw Exceptions from their constructor - I'd get rid of these if I were you.
我也是一个仇恨类,它们从构造函数中抛出异常 - 如果我是你,我会摆脱这些。
#6
I did a little googling and found this glob of code. It's a bit more flexible of an approach me thinks
我做了一点谷歌搜索,发现了这个代码。我认为这种方法更灵活一些
Compliments of this article
赞美这篇文章
class SomeOtherException extends Exception {}
public class TurnOffChecking {
private static Test monitor = new Test();
public static void main(String[] args) {
WrapCheckedException wce = new WrapCheckedException();
// You can call f() without a try block, and let
// RuntimeExceptions go out of the method:
wce.throwRuntimeException(3);
// Or you can choose to catch exceptions:
for(int i = 0; i < 4; i++)
try {
if(i < 3)
wce.throwRuntimeException(i);
else
throw new SomeOtherException();
} catch(SomeOtherException e) {
System.out.println("SomeOtherException: " + e);
} catch(RuntimeException re) {
try {
throw re.getCause();
} catch(FileNotFoundException e) {
System.out.println(
"FileNotFoundException: " + e);
} catch(IOException e) {
System.out.println("IOException: " + e);
} catch(Throwable e) {
System.out.println("Throwable: " + e);
}
}
monitor.expect(new String[] {
"FileNotFoundException: " +
"java.io.FileNotFoundException",
"IOException: java.io.IOException",
"Throwable: java.lang.RuntimeException: Where am I?",
"SomeOtherException: SomeOtherException"
});
}
} ///:~
#7
I have experienced the same problem.
我遇到了同样的问题。
In the simplest case you inform the user of the error and suggest to either to repeat or to cancel the operation.
在最简单的情况下,您通知用户错误并建议重复或取消操作。
In general case, workflow is a sequence of actions (I/O including), where each action "assumes" that the previous one had succeeded.
在一般情况下,工作流是一系列动作(包括I / O),其中每个动作“假定”前一个动作成功。
The approach I have chosen is to create a list of 'roll-back' actions. If the workflow succeeds, they are ignored. If an exception occurs, I execute rollbacks and present an exception to the user.
我选择的方法是创建一个“回滚”动作列表。如果工作流程成功,则会忽略它们。如果发生异常,我会执行回滚并向用户显示异常。
This:
- keeps integrity of data
- allows to code much easier
保持数据的完整性
允许编码更容易
Typical function is like:
典型功能如下:
returntype func(blah-blah-blah, Transaction tr)
{
// IO example
Stream s = null;
try
{
s = new FileStream(filename);
tr.AddRollback(File.Delete(filename));
}
finally
{
if (s != null)
s.close();
}
}
Typical usage is:
典型用法是:
Transaction tr = new Transaction();
try
{
DoAction1(blah1, tr);
DoAction2(blah2, tr);
//...
}
catch (Exception ex)
{
tr.ExecuteRollbacks();
// queue the exception message to the user along with a command to repeat all the actions above
}
This is a little bit trickier in real-world, because
这在现实世界中有点棘手,因为
- sometimes it is necessary to obtain a bunch of locks before execution actions
- rollback code should be exception-silent itself (for instance)
有时需要在执行操作之前获取一堆锁
回滚代码应该是异常 - 静默本身(例如)
But I have already get used to this approach, now my applications are more stable.
但我已经习惯了这种方法,现在我的应用程序更加稳定。
#8
Don't just tank your entire application with a RuntimeException
when you encounter an unlikely error condition. RuntimeException
should be reserved for programmer errors, and IOException
is most likely not caused by programmer error.
当遇到不太可能出现的错误情况时,不要只使用RuntimeException对整个应用程序进行处理。应该为程序员错误保留RuntimeException,并且IOException很可能不是由程序员错误引起的。
Instead, encapsulate the lower level exception in a higher level exception and rethrow. Then handle the higher level exception up the call chain.
相反,将较低级别的异常封装在更高级别的异常中并重新抛出。然后在调用链上处理更高级别的异常。
For example:
class SomeClass {
public void doActions() {
try {
someAction();
} catch (HigherLevelException e) {
notifyUser();
}
someOtherUnrelatedAction();
}
public void someAction() throws HigherLevelException {
try {
// user lower-level abstraction to do our bidding
} catch(LowerLevelException e) {
throw new HigherLevelException(e);
}
}
public void someOtherUnrelatedAction() {
// does stuff
}
}
Most likely the call stack that threw the exception was performing some task in your application. Instead of force crashing your entire application with a RuntimeException
figure out what to do when the problem occurs during that task. For example, were you trying to save a file? Don't crash, instead notify the user there was an issue.
抛出异常的调用堆栈很可能是在您的应用程序中执行某些任务。而不是使用RuntimeException强制崩溃整个应用程序,而不是在该任务期间发生问题时要做什么。例如,您是否尝试保存文件?不要崩溃,而是通知用户存在问题。