关于例外

Java几十年前问世时,它当时是相当创新的。 特别是,它的异常处理机制比以前的C / C ++有了很大的改进。 例如,为了读取文件,可能会发生很多异常:文件可能不存在,文件可能是只读的,等等。<!-more-→

关联的类似Java的伪代码类似于:

Filefile=newFile("/path");if(!file.exists){System.out.println("File doesn't exist");
}elseif(!file.canRead()){System.out.println("File cannot be read");
}else{// Finally read the file// Depending on the language// This could span seveal lines
}

分离的try catch块背后的想法是将业务代码和异常处理代码分开。

try{Filefile=newFile("/path");// Finally read the file// Depending on the language// This could span seveal lines
}catch(FileNotFoundExceptione){System.out.println("File doesn't exist");
}catch(FileNotReadableExceptione){System.out.println("File cannot be read");
}

当然,上面的代码是无用的独立代码。 它可能构成了读取文件的专用方法的主体。

publicStringreadFile(Stringpath){if(!file.exists){returnnull;}elseif(!file.canRead()){returnnull;}else{// Finally read the file// Depending on the language// This could span seveal linesreturncontent;}
}

上述catch块的问题之一是它们返回null 。 因此:

  1. 调用代码需要每次检查是否为null
  2. 无法知道是否未找到文件,或者文件是否可读。

使用更实用的方法可以解决第一个问题,因此可以组合方法。

publicOptional<String>readFile(Stringpath){if(!file.exists){returnOptional.empty();}elseif(!file.canRead()){returnOptional.empty();}else{// Finally read the file// Depending on the language// This could span seveal linesreturnOptional.of(content);}
}

可悲的是,它对第二个问题没有任何改变。 纯粹的功能性方法的追随者可能会舍弃先前的代码段,而采用类似这样的方法:

publicEither<String,Failure>readFile(Stringpath){if(!file.exists){returnEither.right(newFileNotFoundFailure(path));}elseif(!file.canRead()){returnEither.right(newFileNotReadableFailure(path));}else{// Finally read the file// Depending on the language// This could span seveal linesreturnEither.left(content);}
}

而且,与之前的代码相比,它有了很大的改进。 由于返回值的正确部分,它可以准确说明失败的原因(如果失败了),因此它现在更具意义。

不幸的是,仍然存在一个问题,而不是一个很小的问题。 那调用代码呢? 它需要处理失败。 或更可能的是,让调用代码来处理它,依此类推,直到最顶层的代码。 对我来说,这使得不可能将无异常功能方法视为一种改进。

例如,这就是Go中发生的情况:

items,err:=todo.ReadItems(file)iferr!=nil{fmt.Errorf("%v",err)}

如果代码在这里结束,这很好。 但是,否则,必须如上所述将err一直传递到调用代码。 当然,这里有panic关键字,但是看来这不是处理异常的首选方法。

最奇怪的是,这正是人们抱怨Java的检查异常的原因:必须在它们出现的确切位置处处理它们,并且方法签名必须相应地更改。

因此,我都赞成未检查的异常。 这些方法的唯一缺点是它们破坏了纯函数式编程-抛出异常被认为是副作用。 除非您使用的是纯粹的功能方法,否则没有动机避免未检查的异常。

而且,语言和框架可以提供挂钩来处理最顶层的异常。 例如,在JVM上,它们包括:

  • 在JDK中, Thread.setDefaultUncaughtExceptionHandler()
  • 在Vaadin中, VaadinSession.setErrorHandler()
  • 在Spring MVC中, @ExceptionHandler
  • 等等

这样,您可以让您的异常冒泡到应有的处理方式。 拥抱(未经检查的)异常!

翻译自: https://blog.frankel.ch/exceptions/


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部