为什么我可以动态调用继承的 class public 方法,但不能动态调用受保护的方法?

Why can I dynamically call an inherited class public method, but not a protected one?

问题比较复杂,举个例子给大家解释一下比较容易:

考虑以下 ZCL_FOO class:

CLASS zcl_foo DEFINITION
  PUBLIC
  CREATE PUBLIC .

  PUBLIC SECTION.
    METHODS: bar
      RETURNING VALUE(return) TYPE string,
      constructor.
  PROTECTED SECTION.
    DATA: mv_dynamic_method TYPE string.
    METHODS: protected_bar.
  PRIVATE SECTION.
ENDCLASS.

CLASS zcl_foo IMPLEMENTATION.

  METHOD constructor.
    mv_dynamic_method = 'PROTECTED_BAR'.
  ENDMETHOD.


  METHOD bar.
    CALL METHOD (mv_dynamic_method).

    return = mv_dynamic_method.
  ENDMETHOD.


  METHOD protected_bar.
    WRITE 'protected_bar'.
  ENDMETHOD.
ENDCLASS.

以及继承自ZCL_FOOZCL_QUXclass:

CLASS zcl_qux DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC
  INHERITING FROM zcl_foo .

  PUBLIC SECTION.
    METHODS constructor.
  PROTECTED SECTION.
    METHODS xyz.
  PRIVATE SECTION.

ENDCLASS.



CLASS ZCL_QUX IMPLEMENTATION.


  METHOD xyz.
    WRITE 'XYZ'.
  ENDMETHOD.


  METHOD constructor.
    super->constructor( ).
    mv_dynamic_method = 'XYZ'.
  ENDMETHOD.
ENDCLASS.

请注意 xyz 受保护

如果我执行 qux->bar( ),例如通过 SE24 运行ning 它,我会收到一个简短的转储:CX_SY_DYN_CALL_ILLEGAL_METHOD.

但是,如果我将 xyz 从 PROTECTED 移动到 PUBLIC,我能够 运行 qux->bar( ) 成功。

我尝试将 bar 方法更改为使用 CALL METHOD me->(mv_dynamic_method).,但它也很短转储。

这是 ABAP 错误还是预期功能?在我看来,它不应该short dump。

这是故意的,遵循 object-oriented 设计。

PROTECTED 是一个 one-way 路由:您可以使用它使 parents' 属性在 children 中可见,但反之则不行。您的示例恰好尝试了禁止的相反方向。

更准确地说,任何 class 将永远只能访问自己定义的方法,或作为 public 继承的方法,或受其超级 class 保护的方法。 xyz 既未在 zcl_foo 中定义,也未在超级 class 中定义,因此 zcl_foo 看不到它。

不幸的是,您的示例并没有真正解释为什么您需要该动态调用。 object 方向的典型模式是 zcl_fooxyz 声明为 protected,并且 zcl_qux 覆盖该方法。

可以做的是:

CLASS parent DEFINITION PUBLIC CREATE PUBLIC.
  PUBLIC SECTION.
    METHODS call_sub.
  PROTECTED SECTION.
    DATA method_name TYPE string.
ENDCLASS.

CLASS parent IMPLEMENTATION.

  METHOD call_sub.
    CALL METHOD (method_name).
  ENDMETHOD.

ENDCLASS.

然后在每个 sub-class 中覆盖 call_sub:

CLASS child DEFINITION PUBLIC CREATE PUBLIC
    INHERITING FROM zcl_fh_so_parent.
  PUBLIC SECTION.
    METHODS constructor.
    METHODS call_sub REDEFINITION.
  PROTECTED SECTION.
    METHODS call_me.
ENDCLASS.

CLASS child IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    method_name = `CALL_ME`.
  ENDMETHOD.

  METHOD call_sub.
    CALL METHOD (method_name).
  ENDMETHOD.

  METHOD call_me.
    DATA(success) = 'Hooray!'.
  ENDMETHOD.

ENDCLASS.

但是那个模式对我来说真的没有意义。

只是为了好玩,这是按照您的要求执行的错误方法,但任何人都会告诉您永远不要这样做,这是一个非常糟糕的设计,可能会导致许多潜在问题问题 !

CLASS zcl_foo DEFINITION.
  PUBLIC SECTION.
    METHODS:
      constructor,
      bar
        RETURNING VALUE(return) TYPE string.
  PROTECTED SECTION.
    DATA: mv_dynamic_method TYPE string.
    METHODS: protected_bar.
ENDCLASS.
CLASS zcl_qux DEFINITION
    INHERITING FROM zcl_foo
    FRIENDS zcl_foo. " <==== so that ZCL_FOO may use private/protected members of ZCL_QUX
  PUBLIC SECTION.
    METHODS constructor.
  PROTECTED SECTION.
    METHODS xyz.
ENDCLASS.

CLASS zcl_foo IMPLEMENTATION.
  METHOD constructor.
    mv_dynamic_method = 'PROTECTED_BAR'.
  ENDMETHOD.
  METHOD bar.
    DATA(cast) = CAST object( me ).        " <=========== Cast needed
    CALL METHOD cast->(mv_dynamic_method). " <=========== Cast needed
    return = mv_dynamic_method.
  ENDMETHOD.
  METHOD protected_bar.
    WRITE 'protected_bar'.
  ENDMETHOD.
ENDCLASS.

CLASS zcl_qux IMPLEMENTATION.
  METHOD xyz.
    WRITE 'XYZ'.
  ENDMETHOD.
  METHOD constructor.
    super->constructor( ).
    mv_dynamic_method = 'XYZ'.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(qux) = NEW zcl_qux( ).
  qux->bar( ).