Java 反对已检查异常的案例
多年来,我一直无法对以下问题找到一个恰当的答案:为什么一些开发人员如此反对检查异常?我有过无数次的对话,在博客上读过一些东西,读过布鲁斯·埃克尔(Bruce Eckel)说过的话(我看到的第一个公开反对他们的人) 我目前正在编写一些新代码,并非常仔细地注意如何处理异常。我试图看到“我们不喜欢检查异常”人群的观点,但我仍然看不到 我的每一次谈话都以同一个问题结束,而这个问题没有得到回答。。。让我来设置一下: 总的来说(从Java的设计方式来看)Java 反对已检查异常的案例,java,exception,checked-exceptions,Java,Exception,Checked Exceptions,多年来,我一直无法对以下问题找到一个恰当的答案:为什么一些开发人员如此反对检查异常?我有过无数次的对话,在博客上读过一些东西,读过布鲁斯·埃克尔(Bruce Eckel)说过的话(我看到的第一个公开反对他们的人) 我目前正在编写一些新代码,并非常仔细地注意如何处理异常。我试图看到“我们不喜欢检查异常”人群的观点,但我仍然看不到 我的每一次谈话都以同一个问题结束,而这个问题没有得到回答。。。让我来设置一下: 总的来说(从Java的设计方式来看) 错误是指不应该被抓到的东西(VM对花生过敏,有人把
是指不应该被抓到的东西(VM对花生过敏,有人把一罐花生掉在上面)错误
是针对程序员做错的事情(程序员从数组末尾走出来)RuntimeException
(除了Exception
)用于程序员无法控制的事情(写入文件系统时磁盘已满,进程的文件句柄已达到限制,您无法再打开任何文件)RuntimeException
只是所有异常类型的父级Throwable
IOException
,不如将IOException
转换成一个更适合当前情况的异常
使用catch(Exception)
(或者在某些情况下使用catch(Throwable)
包装Main以确保程序可以正常退出,我没有任何问题,但是我总是捕获我需要的特定异常。这样做至少可以显示适当的错误消息
人们永远不会回答的问题是:
如果抛出RuntimeException
子类而不是Exception
子类,那么你怎么知道呢
你应该接住吗
如果答案是catchException
,那么您处理程序员错误的方式与处理系统异常的方式相同。我认为这是错误的
如果您捕获了Throwable,那么您正在以相同的方式处理系统异常和VM错误(以及类似的错误)。这在我看来是错误的
如果答案是只捕获已知抛出的异常,那么如何知道抛出了哪些异常?当程序员X抛出一个新异常并忘记捕获它时会发生什么?这对我来说似乎非常危险
我想说,显示堆栈跟踪的程序是错误的。不喜欢检查异常的人不会有这种感觉吗
所以,如果你不喜欢检查异常,你能解释一下为什么不喜欢,并回答一个没有得到回答的问题吗
我不是在寻找何时使用这两种模型的建议,我要寻找的是为什么人们从
RuntimeException
扩展,因为他们不喜欢从Exception
扩展,和/或为什么他们捕获一个异常,然后重新抛出一个RuntimeException
,而不是向他们的方法添加抛出不喜欢检查异常的动机。我最初同意你的观点,因为我一直支持检查异常,并开始思考为什么我不喜欢在.Net中没有检查异常。但后来我意识到我实际上不喜欢检查异常
为了回答您的问题,是的,我喜欢我的程序显示堆栈跟踪,最好是真正丑陋的堆栈跟踪。我希望应用程序爆炸成一堆可怕的、您可能希望看到的最丑陋的错误消息
原因是,如果它这样做了,我必须修复它,我必须马上修复它。我想马上知道有问题
您实际处理了多少次异常?我不是说捕获异常--我是说处理异常?写以下内容太容易了:
try {
thirdPartyMethod();
} catch(TPException e) {
// this should never happen
}
我知道你可以说这是一种不好的做法,“答案”是做一些例外的事情(让我猜猜,记录它?),但在现实世界中(tm),大多数程序员就是不这样做
因此,是的,如果我不必这样做,我不想捕捉异常,我希望我的程序在我出错时会轰然爆炸。无声失败是最糟糕的结果。反对检查异常的一个论点(来自joelonsoftware.com):
理由是我认为例外并不比什么更好。
“goto’s”自20世纪60年代以来就被认为是有害的,因为它们创造了
从一个代码点到另一个代码点的突然跳转。事实上,它们是
明显比后藤的更糟:
- 它们在源代码中是不可见的。看一段代码, 包括可能引发或不引发异常的函数,没有 查看可能引发哪些异常以及从何处引发异常的方法。这意味着 即使是仔细的代码检查也不会发现潜在的bug
- 它们为函数创建了太多可能的退出点
代码,您确实必须考虑所有可能的代码路径
您的函数。每次调用可以引发
例外,而不是当场抓住它,你为它创造了机会
由终止函数引起的意外错误
public [int or IOException] writeToStream(OutputStream stream) { [void or IOException] a= stream.write(mybytes); if (a instanceof IOException) return a; return mybytes.length; }
try { httpconn.setRequestMethod("POST"); } catch (ProtocolException e) { throw new CanNeverHappenException("oh dear!"); }
try { // do stuff } catch (AnnoyingcheckedException e) { throw new RuntimeException(e); }
try { // do stuff } catch (AnnoyingCheckedException e) { // do nothing }
if (f = fopen("goodluckfindingthisfile")) { ... } else { // file not found ...
f = fopen("goodluckfindingthisfile"); f.read(); // BANG!
try { f = new FileInputStream("goodluckfindingthisfile"); } catch (FileNotFoundException e) { // deal with it. No really, deal with it! ... // this is me dealing with it }
public FileInputStream(String name) throws FileNotFoundException
public RowData getRowData(int row)
public RowData getRowData(int row) throws CheckedInvalidRowNumberException
if (model.getRowData().getCell(0).isEmpty())
"Error: could not find the file 'goodluckfindingthisfile'"
"Internal error occured: IllegalArgumentException in ...."
public RuntimeException(Throwable cause)
try { overzealousAPI(thisArgumentWontWork); } catch (OverzealousCheckedException exception) { throw new RuntimeException(exception); }
catch (Exception e) { /* TODO deal with this at some point (yeah right) */}
try { conn.close(); } catch (SQLException ex) { // Do nothing }
try { // Run the update code on the Swing thread SwingUtilities.invokeAndWait(() -> { try { // Update UI value from the file system data FileUtility f = new FileUtility(); uiComponent.setValue(f.readSomething()); } catch (IOException e) { throw new UncheckedIOException(e); } }); } catch (InterruptedException ex) { throw new IllegalStateException("Interrupted updating UI", ex); } catch (InvocationTargetException ex) { throw new IllegalStateException("Invocation target exception updating UI", ex); }
private void UpdateValue() { // Ensure the update happens on the UI thread if (InvokeRequired) { Invoke(new MethodInvoker(UpdateValue)); } else { // Update UI value from the file system data FileUtility f = new FileUtility(); uiComponent.Value = f.ReadSomething(); } }
@Override public void clear() { try { backingImplementation.clear(); } catch (CheckedBackingImplException ex) { throw new IllegalStateException("Error clearing underlying list.", ex); } }
try { ... do something ... } catch (Throwable throwable) { ApplicationContext.getExceptionService().handleException("Handle this exception", throwable); }
try{ methodDeclaringCheckedException(); }catch(CheckedException e){ logger.error(e); }
try{ methodDeclaringCheckedException(); }catch(CheckedException e){ exceptionHandler.handleError(e); }
class WidgetList extends AbstractList<Widget> { private static final int SIZE_OF_WIDGET = 100; private final RandomAccessFile file; public WidgetList(RandomAccessFile file) { this.file = file; } @Override public int size() { return (int)(file.length() / SIZE_OF_WIDGET); } @Override public Widget get(int index) { file.seek((long)index * SIZE_OF_WIDGET); byte[] data = new byte[SIZE_OF_WIDGET]; file.read(data); return new Widget(data); } }
@Override public int size() { try { return (int)(file.length() / SIZE_OF_WIDGET); } catch (IOException e) { throw new WidgetListException(e); } } public static class WidgetListException extends RuntimeException { public WidgetListException(Throwable cause) { super(cause); } }
class Util { /** * Throws any {@link Throwable} without needing to declare it in the * method's {@code throws} clause. * * <p>When calling, it is suggested to prepend this method by the * {@code throw} keyword. This tells the compiler about the control flow, * about reachable and unreachable code. (For example, you don't need to * specify a method return value when throwing an exception.) To support * this, this method has a return type of {@link RuntimeException}, * although it never returns anything. * * @param t the {@code Throwable} to throw * @return nothing; this method never returns normally * @throws Throwable that was provided to the method * @throws NullPointerException if {@code t} is {@code null} */ public static RuntimeException sneakyThrow(Throwable t) { return Util.<RuntimeException>sneakyThrow1(t); } @SuppressWarnings("unchecked") private static <T extends Throwable> RuntimeException sneakyThrow1( Throwable t) throws T { throw (T)t; } }
@Override public int size() { try { return (int)(file.length() / SIZE_OF_WIDGET); } catch (IOException e) { throw sneakyThrow(e); } }
try { ... } catch (Throwable t) { // catch everything if (t instanceof IOException) { // handle it ... } else { // didn't want to catch this one; let it go throw t; } }
public void writeTo(OutputStream out) throws IOException;
ByteArrayOutputStream out = new ByteArrayOutputStream(); try { someWidget.writeTo(out); } catch (IOException e) { // can't happen (although we shouldn't ignore it if it does) throw new RuntimeException(e); }
try { ... } catch (SomeStupidExceptionOmgWhoCares e) { e.printStackTrace(); }
try { ... } catch (SomethingWeird e) { logger.log(e); }