[关闭]
@XingdingCAO 2017-12-23T09:48:21.000000Z 字数 2259 阅读 1695

Effective Java(第2版):item 61 —— Throw exceptions appropriate to the abstraction

exception Java


避免直接抛出低层次的异常

看到一个你执行的任务毫无关联的异常是十分恼人的。这种情况通常是:你的程序抛出的异常是从低层次传播而来的。这样的异常不仅是无关的,而且用实现细节污染了高层次的API。假如后续的改进更改了实现细节,那么高层次得到的异常也会随之改变。因此,虽然高层次API没有变动,但因为实现细节体现在了高层次的API中,之前依赖于旧版本中的异常的客户程序就会在新版本失效。

异常转译

为了避免上述问题,高层次应该捕获低层次抛出的异常,然后抛出可以根据高层抽象解释的异常。这就是我们常说的异常转译

  1. // Exception Translation
  2. try {
  3. // Use lower-level abstraction to do our bidding
  4. ...
  5. } catch(LowerLevelException e) {
  6. throw new HigherLevelException(...);
  7. }

java.util.AbstractSequentialList<E>就是一个很好的例子。它是List<E>的一个骨架实现。

  1. /**
  2. * This class provides a skeletal implementation of the <tt>List</tt>
  3. * interface to minimize the effort required to implement this interface
  4. * backed by a "sequential access" data store (such as a linked list). For
  5. * random access data (such as an array), <tt>AbstractList</tt> should be used
  6. * in preference to this class.<p>
  7. */
  8. public abstract class AbstractSequentialList<E> extends AbstractList<E> {
  9. /**
  10. * Returns the element at the specified position in this list.
  11. *
  12. * <p>This implementation first gets a list iterator pointing to the
  13. * indexed element (with <tt>listIterator(index)</tt>). Then, it gets
  14. * the element using <tt>ListIterator.next</tt> and returns it.
  15. *
  16. * @throws IndexOutOfBoundsException {@inheritDoc}
  17. */
  18. public E get(int index) {
  19. try {
  20. return listIterator(index).next();
  21. } catch (NoSuchElementException exc) {
  22. throw new IndexOutOfBoundsException("Index: "+index);
  23. }
  24. }

异常链

一种异常转译的特殊形式叫做异常链。适用于低层次的异常可能对于调试由于高层次异常产生的问题有用,低层次异常传递给高层次异常来供其访问。

  1. // Exception Translation
  2. try {
  3. // Use lower-level abstraction to do our bidding
  4. ...
  5. } catch(LowerLevelException cause) {
  6. throw new HigherLevelException(cause);
  7. }

高层次的异常构造方法将传入的cause参数传递给chaining-aware(异常链感知)的父类构造方法,最终传递至一个chaining-aware(异常链感知)的构造方法。cause参数的类型通常是Throwable

  1. // Exception with chaining-aware constructor
  2. class HigherLevelException extends Exception {
  3. HigherLevelException(Throwable cause) {
  4. super(cause);
  5. }
  6. }

大多数标准异常都提供了chaining-aware(异常链感知)的构造方法。如果没有提供,那么你可以使用Throwable接口中的initCause方法。不仅异常链让你可以在程序上访问异常产生的原因(getCause方法),而且它将异常原因的堆栈跟踪集成在了高层次异常中。

避免异常转译的过度使用

异常转译对于无脑(mindless)地直接抛出低层次异常已经大有改善,但是也不应该过度地滥用。最好的处理方式其实是避免在低层次抛出异常,为了实现这一目标,高层次在将参数传递给低层次之前,应当验证获取的参数的正确性。

如果不能保证低层次不抛出异常,低一等的处理方式就是让高层次静默处理这些异常,将高层次的调用者与低层次的异常隔离开来。这一情况下,将异常记录在日志(例如:使用java.util.logging)可能是恰当的,这样做可以使得管理人员调查问题,而客户程序和端用户则无从知晓。

尾巴

总之,如果没有办法在低层次阻止或处理异常,那么使用在高层次使用异常转译是恰当的。除非能够保证所有低层次异常都对于高层次是有用的,那么可以直接抛出。
异常链则为两端提供了最好的解决方式:既允许抛出恰当的高层次异常,又捕获了低层次的原因来用于错误分析。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注