ABAP 中持续存在的烦恼是,无法区分从未设置的变量和设置为其数据类型初始值的变量,并且通过itab[...]
表达式对内部表的访问失败会引发CX_SY_ITAB_LINE_NOT_FOUND
这在运行时处理方面成本很高。
对于枚举类型,可以将第一个变体定义为invalid
以防止第一个混淆:
TYPES:
BEGIN OF ENUM my_bool,
invalid,
true,
false,
END OF ENUM my_bool.
(实际上不要将此类型用作ABAP_BOOL
替代品。谓词方法调用的行为方式,返回my_bool
meth
的方法将导致IF obj->meth( ).
内部的obj->meth( )
只有在返回invalid
时才是真实的,因为谓词方法调用等价于IF obj->meth( ) IS NOT INITIAL.
)
对于非枚举类型,我们运气不好。尝试实现类似选项类型的东西以从尝试访问某些数据但失败的方法返回,被泛型的弱类型阻止:
CLASS zcl_option DEFINITION
FINAL.
PUBLIC SECTION.
CLASS-METHODS some
IMPORTING VALUE(val) TYPE any
RETURNING VALUE(option) TYPE REF TO zcl_option.
CLASS-METHODS none
RETURNING VALUE(option) TYPE REF TO zcl_option.
METHODS is_some
RETURNING VALUE(is_some) TYPE abap_bool.
METHODS get
RETURNING VALUE(val) TYPE ???.
ENDCLASS.
尝试在get
的返回值中使用泛型类型失败,并显示"返回参数必须完全类型化"。
有没有办法在 ABAP 中构造类似选项类型的东西,甚至更一般的总和类型?
这就是我想出的。当它开始看起来不是100% 无用时,我正要把它扔掉,所以它在这里:D :D
PROGRAM ztest_options_tongue_in_cheek.
*&---------------------------------------------------------------------*
TYPES:
BEGIN OF ENUM t_options,
some,
none,
END OF ENUM t_options.
DEFINE macro_option_type.
CLASS zcl_option_&1 DEFINITION FINAL.
PUBLIC SECTION.
METHODS:
constructor
IMPORTING VALUE(i_val) TYPE &1
VALUE(i_option) TYPE t_options,
is_some
RETURNING VALUE(e_is_some) TYPE abap_bool,
get
RETURNING VALUE(e_val) TYPE &1.
PRIVATE SECTION.
DATA:
lv_the_value TYPE &1,
lv_is_none TYPE abap_bool.
ENDCLASS.
CLASS zcl_option_&1 IMPLEMENTATION.
METHOD constructor.
lv_the_value = i_val.
lv_is_none = COND #( WHEN i_option = none THEN abap_true ELSE abap_false ).
ENDMETHOD.
METHOD is_some.
e_is_some = COND #( WHEN lv_is_none = abap_false THEN abap_true ELSE abap_false ).
ENDMETHOD.
METHOD get.
e_val = lv_the_value.
ENDMETHOD.
ENDCLASS.
END-OF-DEFINITION.
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
* Static declaration of required option types
macro_option_type:
i, string, char256, float, uzeit, datum.
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
DEFINE option.
* Rely on garbage collector for simplicity
&2 = NEW zcl_option_&1( i_val = CONV &1( &3 ) i_option = &4 ).
END-OF-DEFINITION.
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
DEFINE declare_init_option.
DATA &2 TYPE REF TO zcl_option_&1.
option &1 &2 space none.
END-OF-DEFINITION.
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
START-OF-SELECTION.
*&---------------------------------------------------------------------*
PERFORM test_options.
*&---------------------------------------------------------------------*
*& Form TEST_OPTIONS
*&---------------------------------------------------------------------*
FORM test_options .
declare_init_option:
i lo_integer_option,
string lo_string_option,
float lo_float_option,
uzeit lo_time_option.
option i lo_integer_option 123 some.
option string lo_string_option 'I am now a string' some.
option string lo_string_option `` none. "back to none
* e.g.,
IF lo_integer_option->is_some( ) = abap_true.
WRITE: / |lo_integer_option is { lo_integer_option->get( ) }|.
ELSE.
WRITE: / 'lo_integer_option is nothing'.
ENDIF.
ENDFORM.
是的,这是可能的。但是,它不像支持泛型类型的编程语言那样类型安全,因此方便。
CLASS zcl_optional DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS constructor
IMPORTING
value TYPE simple OPTIONAL.
METHODS is_available
RETURNING
VALUE(result) TYPE abap_bool.
METHODS put_into
IMPORTING
reference TYPE REF TO data.
PRIVATE SECTION.
DATA value TYPE REF TO data.
ENDCLASS.
CLASS zcl_optional IMPLEMENTATION.
METHOD constructor.
IF value IS SUPPLIED.
DATA(type_descriptor) =
CAST cl_abap_datadescr( cl_abap_datadescr=>describe_by_data( value ) ).
CREATE DATA me->value TYPE HANDLE type_descriptor.
ASSIGN me->value->* TO FIELD-SYMBOL(<target>).
<target> = value.
ENDIF.
ENDMETHOD.
METHOD is_available.
result = xsdbool( me->value IS BOUND ).
ENDMETHOD.
METHOD put_into.
IF value IS BOUND.
ASSIGN value->* TO FIELD-SYMBOL(<source>).
ASSIGN reference->* TO FIELD-SYMBOL(<target>).
<target> = <source>.
ENDIF.
ENDMETHOD.
ENDCLASS.
像这样使用
DATA(optional_integer) = NEW zcl_optional( 42 ).
cl_abap_unit_assert=>assert_true( optional_integer->is_available( ) ).
DATA integer_variable TYPE i.
optional_integer->put_into( REF #( integer_variable ) ).
cl_abap_unit_assert=>assert_equals( act = integer_variable exp = 42 ).
DATA(optional_string) = NEW zcl_optional( `abc` ).
cl_abap_unit_assert=>assert_true( optional_string->is_available( ) ).
DATA string_variable TYPE string.
optional_string->put_into( REF #( string_variable ) ).
cl_abap_unit_assert=>assert_equals( act = string_variable exp = `abc` ).
DATA(empty_optional) = NEW zcl_optional( ).
cl_abap_unit_assert=>assert_false( empty_optional->is_available( ) ).
DATA another_variable TYPE string.
empty_optional->put_into( REF #( another_variable ) ).
cl_abap_unit_assert=>assert_initial( another_variable ).
问题是返回可选的方法无法描述内容的实际类型。
METHODS do_something
RETURNS
VALUE(result) TYPE REF TO zcl_optional. " int4 or string?
内部表的初始值始终是没有行的空表。所以你的整个想法都没有意义。
设置初始值不会阻止您获得异常CX_SY_ITAB_LINE_NOT_FOUND
。