在 ABAP 中,Java 的 finally 关键字等价于什么?

In ABAP, what is the equivalent of Java's finally keyword?

在Java中,finally关键字用于执行代码(与异常一起使用-try..catch语句)无论是否抛出异常(source).

例如:

try {
    // this code might throw an exception
    riskyCall();

    // this code will only run if no exception was thrown above
    mainProgram();
}
finally {
    // this code will always run
    cleanUp();
}

ABAP 中是否有等效的功能?如果不是,实现相同功能的惯用方法是什么?

我知道 ABAP 有一个 CLEANUP 关键字,但这似乎只有在抛出异常时才会执行。

我进行了试验并发现了以下可能的解决方案。不幸的是,我想不出没有代码重复的任何解决方案。

METHOD risky_method.
    TRY.
       WRITE 'code before...'.
       IF lv_error_condition = abap_true.
         RAISE EXCEPTION TYPE cx_foo.
       ENDIF.
       WRITE 'Main program...'.
       WRITE 'Cleanup...'.
    CLEANUP.
       WRITE 'Cleanup...'.
    ENDTRY.
ENDMETHOD.

METHOD outer_scope.
    TRY.
        risky_method( ).
    CATCH cx_foo INTO DATA(lx_foo).
        WRITE 'Caught the error!'.
    ENDTRY.
ENDMETHOD.

对于lv_error_condition等于abap_false的情况,执行方法outer_scope的输出是:

code before... Main program... Cleanup...

对于lv_error_condition等于abap_true的情况,输出为:

code before... Cleanup... Caught the error!

此解决方案的优点是清理始终运行。它的缺点是需要一些代码重复,因为清理需要编写两次。如果清理被打包到一个方法中,那么它就不是可怕的代码重复量。 :-/

ABAP 没有 Java finally 块的确切等价物。

有一个 TRY ... CLEANUP 结构,乍一看很相似,但实际上工作起来却大不相同:

METHOD buggy_method.
    TRY.
       WRITE 'code before the error...'.
       RAISE EXCEPTION TYPE cx_foo.
       WRITE 'This line will not get executed.'.
    CLEANUP.
       WRITE 'Cleanup...'.
    ENDTRY.
ENDMETHOD.

" ...on an outer scope...
TRY.
    buggy_method( ).
  CATCH cx_foo INTO DATA(lx_foo).
    WRITE 'Caught the error!'.
ENDTRY.

输出:code before the error... Cleanup... Caught the error!

然而,清理块并不总是*得到执行。它仅在出现异常时执行,并且该异常 not 由同一 TRY 块中的 CATCH 处理。这个想法是将它用于清理,清理应该在外部级别的 TRY 块处理异常时发生。因此,无论您是否有错误,它对于您想要 运行 的代码都没有用。它仅对您想要 运行 的代码有用,以防出现 try 块无法自行处理的错误。


涵盖 Java finally 块的一些用例的另一个功能是 resumable exceptions:

" In the class definiton
METHODS buggy_method RAISING RESUMABLE(cx_foo).

" In the class implementation:
METHOD buggy_method.
   WRITE 'code before the error...'.
   RAISE RESUMABLE EXCEPTION TYPE cx_foo.
   WRITE 'code after the error....'.  
ENDMETHOD.

" Somewhere else:
TRY.
    buggy_method( ).
  CATCH BEFORE UNWIND cx_foo INTO DATA(lx_foo).
    WRITE 'Caught the error!'.
    RESUME.
ENDTRY.

输出:code before the error... Caught the error! code after the error....

CATCH 块末尾的 RESUME 关键字导致在异常 RAISE RESUMABLEd 后立即继续执行。因此,如果您想确保即使在出现错误的情况下也能执行方法的结尾,那么这可能就是您正在寻找的语法。

* 是的,我知道有一些奇特的边缘情况,其中 Java 中的 finally-block 不会被执行。这些与这里无关。

一位同事给了我解决方案。避免代码重复,同时仍然允许异常向上传播的技巧是根本不使用 CLEANUP 关键字,而是:

  1. 在有风险的代码之后,捕获异常CX_ROOT(捕获所有可能的异常,而不仅仅是您期望的异常!)
  2. 运行 finally 风格的清理代码
  3. 使用IS BOUND查看是否引发异常,如果是,则重新引发

这是一个例子:

METHOD risky_method.
    TRY.
        WRITE 'code before...'.
        IF lv_error_condition = abap_true.
            RAISE EXCEPTION TYPE cx_foo.
        ENDIF.
        WRITE 'Main program...'.
    CATCH cx_root INTO DATA(lx_root).
    ENDTRY.

    WRITE 'Cleanup...'.

    IF lx_root IS BOUND.
        RAISE lx_root.
    ENDIF.
ENDMETHOD.

METHOD outer_scope.
    TRY.
        risky_method( ).
    CATCH cx_foo INTO DATA(lx_foo).
        WRITE 'Caught the error!'.
    ENDTRY.
ENDMETHOD.

对于lv_error_condition等于abap_false的情况,输出为:

code before... Main program... Cleanup...

对于lv_error_condition等于abap_true的情况,输出为:

code before... Cleanup... Caught the error!