代码之家  ›  专栏  ›  技术社区  ›  gustafc

我的安全重发有多安全?

  •  11
  • gustafc  · 技术社区  · 15 年前

    ( 后期编辑: 当Java 7到来时,这个问题有望过时,因为 "final rethrow" feature 哪一个 seems like it will be added )


    很多时候,我发现自己的处境是这样的:

        do some initialization
        try {
            do some work 
        } catch any exception {
            undo initialization
            rethrow exception
        }
    

    在C,你可以这样做:

    InitializeStuff();
    try
    {
        DoSomeWork();
    }
    catch 
    {
        UndoInitialize();
        throw;
    }
    

    对于Java来说,没有好的替代,而且 the proposal for improved exception handling was cut from Java 7 ,看来我们最多要过几年才能找到类似的东西。因此,我决定自己滚:

    ( 编辑: 半年后, final rethrow is back ,或者看起来是这样。)

    public final class Rethrow {
    
        private Rethrow() { throw new AssertionError("uninstantiable"); }
    
        /** Rethrows t if it is an unchecked exception. */
        public static void unchecked(Throwable t) {
            if (t instanceof Error)
                throw (Error) t;
            if (t instanceof RuntimeException)
                throw (RuntimeException) t;
        }
    
        /** Rethrows t if it is an unchecked exception or an instance of E. */
        public static <E extends Exception> void instanceOrUnchecked(
                Class<E> exceptionClass, Throwable t) throws E, Error,
                RuntimeException {
            Rethrow.unchecked(t);
            if (exceptionClass.isInstance(t))
                throw exceptionClass.cast(t);
        }
    
    }
    

    典型用法:

    public void doStuff() throws SomeException {
        initializeStuff();
        try {
            doSomeWork();
        } catch (Throwable t) {
            undoInitialize();
            Rethrow.instanceOrUnchecked(SomeException.class, t);
            // We shouldn't get past the above line as only unchecked or 
            // SomeException exceptions are thrown in the try block, but
            // we don't want to risk swallowing an error, so:
            throw new SomeException("Unexpected exception", t); 
        }
        private void doSomeWork() throws SomeException { ... }
    }
    

    有点罗嗦,吸引人 Throwable 通常是不高兴的,我并不真的很高兴用反思来重新提出一个例外,我总是感到有点不安,写“这不会发生”的评论,但在实践中,它运作良好(或似乎,至少)。我想知道的是:

    1. 我的rethrow助手方法有缺陷吗?我错过了一些角落的案子?(我知道 可投掷的 可能是因为一些非常严重的事情 undoInitialize 会失败,但没关系。)
      • 有人已经发明了这个吗?我看了下议院的朗 ExceptionUtils 但那还有别的事。

    编辑:

    • finally 不是我要找的机器人。我只想在抛出异常时做些事情。
    • 是的,我知道接球 可投掷的 是一个大的不不,但我认为这是比有三个捕获条款(为 Error , RuntimeException SomeException ,分别)具有相同的代码。
    • 请注意,我并没有试图抑制任何错误,我的想法是 try 一旦我重新连接了一些东西,block将继续在调用堆栈中冒泡起来。
    3 回复  |  直到 12 年前
        1
  •  5
  •   Peter Lawrey    15 年前

    有几种方法可以解决这个问题。第一个是我的偏好,如果你不需要知道什么是例外。

    boolean okay = false;
    try {
      // do some work which might throw an exception
      okay = true;
    } finally {
      if (!okay) // do some clean up.
    }
    

    在某些情况下,您可以在没有额外变量的情况下执行同样的操作,这取决于测试块所做的操作。

    第二个选择是黑客,但也有效。

    try {
        // do some work which might throw an exception
    } catch (Throwable t) {
        // do something with t.
        Thread.currentThread().stop(t);
    }
    

    stop(throwable t)方法不会停止线程,而是导致线程以未经检查的方式抛出异常。

    您可以使用unsafe.throwexception()来进行一些修改,但有一种方法可以使用我已经忘记的泛型。

        2
  •  1
  •   James Black    15 年前

    如果您担心会发生未初始化的情况,那么您可能只想将该代码放入finally块中,因为,如果应该在某个点调用它,那么您可能应该始终清理。

    我不太会抓到 Throwable 正如我想处理的一些异常,以及一些我刚刚记录的,因为用户无法做任何事情,比如As,都没有用过的异常。 NullPointerException .

    但是,你没有表现出 SomeException 定义为,但如果 OutOfMemoryException 被抛出,你的抛出物将捕获它,但它可能与 某个例外 所以在示例函数中需要包装器,至少当我查看 instanceOrUnchecked 方法。

    您可能希望编写一个单元测试,尝试不同类型的异常,并查看预期的工作或不工作,这样您就可以记录预期的行为。

        3
  •  1
  •   Yishai    15 年前

    另一种选择是拥有一个工厂,该工厂仅在原因是选中的异常时创建异常:

       public static SomeException throwException(String message, Throwable cause) throws SomeException {
          unchecked(cause); //calls the method you defined in the question.
          throw new SomeException(message, cause);
       }
    

    我在方法中放入返回值的原因是客户端可以这样做:

         catch (Throwable e) {
             undoInitialize();
             throw SomeException.throwException("message", e);
         }
    

    这样,如果方法具有返回类型,编译器就被愚弄成不需要在catch语句之后返回,但如果客户端忘记在调用factory方法之前放置throw,编译器仍然抛出异常。

    与代码相比,这种方法的缺点是可移植性较差(它适用于某个异常,但不适用于某个其他异常),但这可能没问题,因为对于需要进行撤消初始化的每个异常类型,它都不适用。

    如果它适合您的用例,您可以将未检查的调用放入someexception的构造函数中,并使逻辑可用于所有子类,但这必须适合您的特定项目-在一般情况下,这不是一个好主意,因为它会阻止包装运行时异常。

          public SomeException(message, cause) {
                super(message, unchecked(cause));
          }
    
          private static Throwable unchecked(Throwable cause) {
              if (cause instanceof Error) throw (Error) cause;
              if (cause instanceof RuntimeException) throw (RuntimeException) cause;
              return cause;
          }