枚举对象是枚举类型的数据对象。枚举对象只能包含类型为枚举类型的枚举值。ABAP从版本7.51开始支持它们。
这是一种常见的模式。在ABAP 7.51之前,人们通常用如下方式实现类似的功能:
CLASS cx_wrong_size DEFINITION INHERITING FROM cx_static_check. ENDCLASS. CLASS shirt DEFINITION. PUBLIC SECTION. TYPES tsize TYPE i. CONSTANTS: size_s TYPE tsize VALUE 0, size_m TYPE tsize VALUE 1, size_l TYPE tsize VALUE 2, size_xl TYPE tsize VALUE 3. METHODS constructor IMPORTING size TYPE tsize RAISING cx_wrong_size. ... PRIVATE SECTION. DATA size TYPE tsize. ENDCLASS. CLASS shirt IMPLEMENTATION. METHOD constructor. IF size <> size_s AND size <> size_m AND size <> size_l AND size <> size_xl. RAISE EXCEPTION TYPE cx_wrong_size. ENDIF. me->size = COND #( WHEN size <> size_s AND size <> size_m AND size <> size_l AND size <> size_xl THEN THROW cx_wrong_size( ) ELSE size ). ENDMETHOD. ENDCLASS.
这里,size属性只可以是shirt类中定义的常量中的值。其它值会导致异常。用户创建shirt类时,需要这样做:
TRY. DATA(shirt) = NEW shirt( shirt=>size_xl ). CATCH cx_wrong_size. ... ENDTRY.
看到开销了吗?为什么不让运行时环境来为你做值检查呢?有一种叫做枚举和枚举类型的概念(可以在这种情况下应用)。
在7.51以及更高的版本里,ABAP也会支持枚举概念。如果使用枚举类型来重写上面的例子的话:
CLASS shirt DEFINITION. PUBLIC SECTION. TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. METHODS constructor IMPORTING size TYPE tsize. ... PRIVATE SECTION. DATA size TYPE tsize. ENDCLASS. CLASS shirt IMPLEMENTATION. METHOD constructor. me->size = size. ENDMETHOD. ENDCLASS.
TYPES语句的一个新变式:BEGIN OF ENUM – END OF ENUM,它可以围起一个常量的集。常量的标准基本类型是i并且枚举值从0开始计数。
像上文那样使用的话:
DATA(shirt) = NEW shirt( shirt=>size_xl ).
不过,使用了枚举对象后,你不再需要处理异常了。如果你传递一个非法值的话,会得到语法错误!
DATA(shirt) = NEW shirt( 333 ).
枚举是类型和常量的混合体。通过BEGIN OF ENUM enum – END OF ENUM enum 你声明了一个可以在TYPE附加项后声明的基本类型enum。在它们之间,你可以声明一个常量集,它们叫做枚举常量。这样就定义了拥有enum类型的枚举对象所允许的枚举值。在这里,TYPES实际上就和CONSTANTS语句一样。
ABAP运行时环境会检查只有被允许的枚举值可以被赋给枚举对象。
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA size TYPE tsize. size = size_xl. "允许 DATA dobj LIKE size. dobj = size. "允许 dobj = 333. "语法或运行时错误
枚举对象只能赋值给拥有相同枚举类型的枚举对象。同样,枚举对象间的比较也只能在相同类型的枚举对象间发生。当然,枚举常量本身也包含在内。
通常,你一点也不关心枚举对象的内容。枚举对象的语义由枚举常量来定义。然而,你也可以将枚举类型定义为i类型之外的其它基本类型,并且拥有其它枚举值(其中一个必须是初始值):
TYPES: basetype TYPE c LENGTH 2, BEGIN OF ENUM tsize BASE TYPE basetype, size_i VALUE IS INITIAL, size_s VALUE `S`, size_m VALUE `M`, size_l VALUE `L`, size_xl VALUE `XL`, END OF ENUM tsize. DATA size TYPE tsize. size = size_xl. "允许 DATA dobj LIKE size. dobj = size."允许
这使你可以轻松地把现有“枚举值”转换为新方式的枚举值。运气好的话,可以不需要调整它们的使用。
如果你在一个上下文中有超过一个枚举类型,你可以将各个枚举值组织到结构里:
TYPES: BEGIN OF ENUM tsize STRUCTURE size, s, m, l, xl, END OF ENUM tsize STRUCTURE size. DATA dobj TYPE tsize. dobj = size-xl. "允许
以上代码定义了一个枚举结构size。结构的组件是枚举类型的枚举常量。
枚举对象的常见用处是比较它和枚举常量以决定程序需要切换到哪一个功能分支:
TYPES: BEGIN OF ENUM tsize STRUCTURE size, s, m, l, xl, END OF ENUM tsize STRUCTURE size. DATA dobj TYPE tsize. ... CASE dobj. WHEN size-s. ... WHEN size-m. ... WHEN size-l. ... WHEN size-xl. ... ENDCASE.
除此之外,还有一些可以应用的情况:
你可以将枚举对象赋给一个c类型或者string类型的文本。结果就是定义了枚举值的枚举常量的名字。
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA text TYPE string. text = size_xl. cl_demo_output=>display( text ). "结果是SIZE_XL
也可以写作:
DATA(text) = CONV string( size_xl ).
可以使用CONV操作符指定类型来访问当前值:
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA(value) = CONV i( size_xl ) . cl_demo_output=>display( value ). "输出结果是3
另一种方式是,可以使用CONV将一个有效的枚举值转换为枚举对象(常规的赋值语句是无法做到的):
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA(num) = 3. TRY. DATA(dobj) = CONV tsize( num ) . cl_demo_output=>display( dobj ). "Output is SIZE_XL CATCH cx_sy_conversion_no_enum_value. ... ENDTRY.
RTTI中也多了一个相应的类CL_ABAP_ENUMDESCR:
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA(size) = VALUE tsize( ). DATA(enum_descr) = CAST cl_abap_enumdescr( cl_abap_typedescr=>describe_by_data( size ) ). cl_demo_output=>new( )->write_data( enum_descr->kind "E, for elementary )->write_data( enum_descr->type_kind "k, new for enumerated type )->write_data( enum_descr->base_type_kind "I, the base type )->write_data( enum_descr->members "Table of constants and values )->display( ).
基本上就是这些内容了。
躺下让ABAP运行时来为你工作吧。要注意枚举类型只能在某些合适的运算位置中出现,并且只能包含预先规定的值。非法的枚举值永远不会在枚举对象中出现。
PS:原文的评论中有人提到,枚举值和数据字典中的domain的固定值很像,为什么二者没有集成关系?
作者的回答是:这个特性实际上已经在设计中了,很有希望出现。具体情况要视资源和优先级而定。