|
SAPhtml
——ABAP/4 技术总结 V3.0java
2014-10-14node
--江正军git
1. 基础
1.1. 基本数据类型
C、N、D、T、I、F、P、X、string、Xstring
P:默认为8字节,最大容许16字节。最大整数位:16*2 = 32 - 1 = 31 -14(容许最大小数位数) = 17位整数位
类型 |
最大长度(字符数) |
默认长度 |
说明 |
C |
1~262143个字符 |
1 字符 |
|
N |
1~262143个字符 |
1 字符 |
0到9之间字符组成的数字字符串 |
D |
8 个字符 |
|
日期格式必须为 YYYYMMDD |
T |
6 个字符 |
|
格式为 24-hour的 HHMMSS |
I |
4 bytes |
|
-2.147.483.648 to +2.147.483.647 |
F |
8 bytes |
|
小数位最大能够到17位,便可精确到小数点后17位 |
P |
1 to 16 bytes |
8 bytes |
两个数字位压缩后才占一个字节,因为0-9的数字只须要4Bit位,因此一个字节实质上容许存储二位数字,这就是P数据类型为压缩数据类型的由来。并借用半个字节来存储小数点位置、正号、负号相关信息 |
X |
1~524,287 bytes |
1 byte |
十六进制字符 0-9, A-F具体的范围为:00~FF 类型X是十六进制类型,可表示内存字节实际内容,使用两个十六制字符表示一个字节中所存储的内容。但直接打印输出时,输出的仍是赋值时字面意义上的值,而不是Unicode解码后的字符 若是未在 DATA 语句中指定参数<length>,则建立长度为 1 注:若是值是字母,则必定要大写 |
1.1.1.P类型(压缩型)数据
是一种压缩的定点数,其数据对象占据内存字节数和数值范围取定义时指定的整个数据大小和小数点后位数,若是不指定小数位,则将视为I类型。其有效数字位大小能够是从1~31位数字(小数点与正负号占用一个位置,半个字节),小数点后最多容许14个数字。
P类型的数据,可用于精确运算(这里的精确指的是存储中所存储的数据与定义时字面上所看到的大小相同,而不存在精度丢失问题——看到的就是内存中实实在在的大小)。在使用P类型时,要先选择程序属性中的选项 Fixed point arithmetic(即定点算法,通常默认选中),不然系统将P类型看用整型。其效率低于I或F类型。
"16 * 2 = 32表示了整个字面意义上容许的最大字面个数,而14表示的是字面上小数点后面容许的最大小数位,而不是指14个字节,只有这里定义时的16才表示16个字节
DATA: p(16) TYPE p DECIMALS 14 VALUE '12345678901234567.89012345678901'.
"正负符号与小数点固定要占用半个字节,一个字面上位置,并包括在这16个字节里面。
"16 * 2 = 32位包括了小数点与在正负号在内
"在定义时字面上容许最长能够达到32位,除去小数点与符号需占半个字节之后
"有效数字位可容许31位,这31位中包括了整数位与小数位,再除去定义时小
"数位为14位外,整数位最多还可达到17位,因此下面最多只能是17个9
DATA: p1(16) TYPE p DECIMALS 14 VALUE '-99999999999999999'.
"P类型是以字符串来表示一个数的,与字符串不同的是,P类型中的每一个数字位只会占用4Bit位,因此两个数字位才会占用一个字节。另外,若是定义时没有指定小数位,表示是整型,但小数点固定要占用半个字节,因此不带小数位与符号的最大与最小整数以下(最多容许31个9,而不是32个)
DATA: p1(16) TYPE p VALUE '+9999999999999999999999999999999'.
DATA: p2(16) TYPE p VALUE '-9999999999999999999999999999999'.
其实P类型是以字符串形式来表示一个小数,这样才能够做到精确,就像Java中要表示一个精确的小数要使用BigDecimal同样,不然会丢失精度。
DATA: p(9) TYPE p DECIMALS 2 VALUE '-123456789012345.12'.
WRITE: / p."123456789012345.12-
DATA: f1 TYPE f VALUE '2.0',
f2 TYPE f VALUE '1.1',
f3 TYPE f.
f3 = f1 - f2."不能精确计算
"2.0000000000000000E+00 1.1000000000000001E+00 8.9999999999999991E-01
WRITE: / f1 , f2 , f3.
DATA: p1 TYPE p DECIMALS 1 VALUE '2.0',
p2 TYPE p DECIMALS 1 VALUE '1.1',
p3 TYPE p DECIMALS 1.
p3 = p1 - p2."能精确计算
WRITE: / p1 , p2 , p3. "2.0 1.1 0.9
Java中精确计算:
publicstaticvoid main(String[] args) {
System.out.println(2.0 - 1.1);// 0.8999999999999999
System.out.println(sub(2.0, 0.1));// 1.9
}
publicstaticdouble sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
1.2.TYPE、LIKE
透明表(还有其它数据词典中的类型,如结构)便可看做是一种类型,也可看做是对象,因此便可使用TYPE,也能够使用LIKE:
TYPES type6 TYPE mara-matnr.
TYPES type7 LIKE mara-matnr.
DATA obj6 TYPE mara-matnr.
DATA obj7 LIKE mara-matnr.
"SFLIGHT为表类型
DATA plane LIKE sflight-planetype.
DATA plane2 TYPE sflight-planetype.
DATA plane3 LIKE sflight.
DATA plane4 TYPE sflight.
"syst为结构类型
DATA sy1 TYPE syst.
DATA sy2 LIKE syst.
DATA sy3 TYPE syst-index.
DATA sy4 LIKE syst-index.
注:定义的变量名千万别与词典中的类型相同,不然表面上便可使用TYPE也可以使用LIKE,就会出现这两个关键字(Type、Like)均可用的奇怪现像,下面是定义一个变量时与词典中的结构同名的后果(致使)
DATA : BEGIN OF address2,
street(20) TYPE c,
city(20) TYPE c,
END OF address2.
DATA obj4 TYPE STANDARD TABLE OF address2."这里使用的实质上是词典中的类型address2
DATA obj5 LIKE STANDARD TABLE OF address2."这里使用是的上面定义的变量address2
上面程序编译经过,按理obj4定义是经过不过的(只能使用LIKE来引用另外一定义变量的类型,TYPE是不能够的),但因为address2是数字词典中定义的结构类型,因此obj4使用的是数字词典中的结构类型,而obj5使用的是LIKE,因此使用的是address2变量的类型
1.3. DESCRIBE
DESCRIBE FIELD dobj
[TYPE typ [COMPONENTS com]]
[LENGTH ilen IN {BYTE|CHARACTER} MODE]
[DECIMALS dec]
[OUTPUT-LENGTH olen]
[HELP-ID hlp]
[EDIT MASK mask].
DESCRIBE TABLE itab [KIND knd] [LINES lin] [OCCURS n].
1.4.字符串表达式
能够使用&或&&将多个字符模板串连接起来,能够突破255个字符的限制,下面两个是等效的:
|...| & |...|
|...| && |...|
若是内容只有字面常量文本(没有变量表达式或控制字符\r \n \t),则不须要使用字符模板,可这样(若是包含了这些控制字符时,会原样输出,因此有这些控制字符时,请使用 |...|将字符包起来):
`...` && `...`
可是上面3个与下面3个是不同的:
`...` & `...`
'...' & '...'
'...' && '...'
上面前两个仍是会受255个字符长度限制,最后一个虽然不受255限制,但尾部空格会被忽略
字面常量文本(literal text)部分,使用 ||括起来,不能含有控制字符(如 \r \n \t这些控制字符),特殊字符 |{ } \须要使用 \进行转义:
txt = |Characters \|, \{, and \} have to be escaped by \\ in literal text.|.
字符串表达式:
str = |{ ( 1 + 1 ) * 2 }|."算术计算表达式
str = |{ |aa| && 'bb' }|."字符串表达式
str = |{ str }|."变量名
str = |{ strlen( str ) }|."内置函数
1.5. Data element、Domain
数据元素是构成结构、表的基本组件,域又定义了数据元素的技术属性。Data element主要附带Search Help、Parameter ID、以及标签描述,而类型是由Domain域来决定的。Domain主要从技术方面描述了Data element,如Data Type数据类型、Output Length输出长度、Convers. Routine转换规则、以及Value Range取值范围
将技术信息从Data element提取出来为Domain域的好处:技术信息造成的Domain能够共用,而每一个表字段的业务含意不同,会致使其描述标签、搜索帮助不同,因此牵涉到业务部分的信息直接Data element中进行描述,而与业务无关的技术信息部分则分离出来造成Domain
1.6. 词典预约义类型与ABAP类型映射
当你在ABAP程序中引用了ABAPDictionary,则预置Dictionary类型则会转换为相应的ABAP类型,预置的Dictionary类型转换规则表以下:
Dictionarytype |
Meaning |
Maximumlengthn |
ABAPtype |
DEC |
Calculation/amountfield |
1-31, 1-17intables |
P((n+1)/2) |
INT1 |
Single-byte integer |
3 |
Internalonly |
INT2 |
Two-byteinteger |
5 |
Internalonly |
INT4 |
Four-byteinteger |
10 |
I |
CURR |
Currencyfield货币字段 |
1-17 |
P((n+1)/2) |
CUKY |
Currencykey货币代码 |
5 |
C(5) |
QUAN |
Amount金额 |
1-17 |
P((n+1)/2) |
UNIT |
Unit单位 |
2-3 |
C(n) |
PREC |
Accuracy |
2 |
X(2) |
FLTP |
Floating pointnumber |
16 |
F(8) |
NUMC |
Numeric text数字字符 |
1-255 |
N(n) |
CHAR |
Character字符 |
1-255 |
C(n) |
LCHR |
Long character |
256-max |
C(n) |
STRING |
Stringofvariable length |
1-max |
STRING. |
RAWSTRING |
Byte sequence of variable length |
1-max |
XSTRING |
DATS |
Date |
8 |
D |
ACCP |
Accounting period YYYYMM |
6 |
N(6) |
TIMS |
Time HHMMSS |
6 |
T |
RAW |
Byte sequence |
1-255 |
X(n) |
LRAW |
Long byte sequence |
256-max |
X(n) |
CLNT |
Client |
3 |
C(3) |
LANG |
Language |
internal 1, external 2 |
C(1) |
这里的“容许最大长度m”表示的是字面上容许的字符位数,而不是指底层所占内存字节数,如
int1的取值为0~255,因此是3位(不包括符号位)
int2的取值为-32768~32767,因此是5位
lLCHR and LRAW类型容许的最大值为INT2 最大值
lRAWSTRING and STRING 具备可变长度,最大值能够指定,但没有上限
lSSTRING 长度是可变的,其最大值必须指定且上限为255。与CHAR类型相比其优点是它与ABAP type string进行映射。
这些预置的Dictionary类型在建立Data element、Domain时能够引用
在Unicode系统中,一个字符占两个字节
1.7.字符串处理
SPLIT dobj AT sep INTO { {result1 result2 ...} | {TABLE result_tab} }必须指定足够目标字段。不然,用字段dobj的剩余部分填充最后目标字段并包含分界符;或者使用内表动态接收
SHIFT dobj {[{BY num PLACES}|{UP TO sub_string}][[LEFT|RIGHT][CIRCULAR]]}
| { {LEFT DELETING LEADING}|{RIGHT DELETING TRAILING} } pattern
对于固定长度字符串类型,shift产生的空位会使用空格或十六进制的0(若是为X类型串时)来填充
向右移动时前面会补空格,固定长度类型字符串与String结果是不同:String类型右移后不会被截断,只是字串前面补相应数量的空格,但若是是C类型时,则会截断;左移后后面是否被空格要看是不是固定长度类型的字符串仍是变长的String类型串,左移后C类型会补空格,String类型串不会(会缩短)
CIRCULAR:将移出的字符串放在左边或者左边
pattern:只要前导或尾部字符在指定的pattern字符集里就会被去掉,直到第一个不在模式pattern的字符止
CONDENSE <c> [NO-GAPS].若是是C类型只去掉前面的空格(由于是定长,即便后面空格去掉了,左对齐时后面会补上空格),若是是String类型,则后面空格也会被去掉;字符串中间的多个连续的空格使用一个空格替换(String类型也是这样);NO-GAPS:字符串中间的全部空格都也都会去除(String类型也是这样);空格去掉后会左对齐
CONCATENATE {dobj1 dobj2 ...}|{LINES OF itab}
INTO result
[SEPARATED BY sep]
[RESPECTING BLANKS].
CDNT类型的前导空格会保留,尾部空格都会被去掉,但对String类型全部空格都会保留;对于c, d, n, t类型的字符串有一个RESPECTING BLANKS选项可以使用,表示尾部空格也会保留。注:使用 `` 对String类型进行赋值时才会保留尾部空格 字符串链接能够使用 && 来操做,具体请参考这里
strlen(arg)、Xstrlen(arg)String类型的尾部空格会计入字符个数中,但C类型的变量尾部空格不会计算入
substring( val = TEXT [off = off] [len = len] )
count( val = TEXT {sub = substring}|{regex = regex} )匹配指定字符串substring或正则式regex出现的子串次数,返回的类型为i整型类型
contains( val = TEXT REGEX = REGEX)是否包含。返回布尔值,注:只能用在if、While等条件表达式中
matches( val = TEXT REGEX = REGEX)regex表达式要与text彻底匹配,这与contains是不同的。返回布尔值,也只能用在if、While等条件表达式中
match( val = TEXT REGEX = REGEX occ = occ)返回的为匹配到的字符串。注:每次只匹配一个。occ:表示需匹配到第几回出现的子串。若是为正,则从头日后开始计算,若是为负,则从尾部向前计算
find( val = TEXT {sub = substring}|{regex = regex}[occ = occ] )查找substring或者匹配regex的子串的位置。若是未找到,则返回 -1,返回的为offset,因此从0开始
FIND ALL OCCURRENCES OF REGEX regex IN dobj
[MATCH COUNT mcnt] 成功匹配的次数
{ {[MATCH OFFSET moff][MATCH LENGTH mlen]}最后一次总体匹配到的串(总体串,最外层分组,而不是指正则式最内最后一个分组)起始位置与长度
| [RESULTS result_tab|result_wa] } result_tab接收全部匹配结果,result_wa只能接收最后一次匹配结果
[SUBMATCHES s1 s2 ...].一般与前面的MATCH OFFSET/ LENGTH一块儿使用。只会接收使用括号进行分组的子组。若是变量s1 s2 ...比分组的数量多,则多余的变量被initial;若是变量s1 s2 ...比分组的数量少,则多余的分组将被忽略;且只存储第一次或最后一次匹配到的结果
replace( val = TEXT REGEX = REGEX WITH = NEW)使用new替换指定的子符串,返回String类型
REPLACE ALL OCCURRENCES OF REGEX regex IN dobj WITH new
1.7.1. count、match结合
DATA: text TYPE string VALUE `Cathy's cat with the hat sat on Matt's mat.`,
regx TYPE string VALUE `\<.at\>`."\< 单词开头,\> 单词结尾
DATA: counts TYPE i,
index TYPE i,
substr TYPE string.
WRITE / text.
NEW-LINE.
counts = count( val = text regex = regx )."返回匹配次数
DO counts TIMES.
index = find( val = text regex = regx occ = sy-index )."返回匹配到的的起始位置索引
substr = match( val = text regex = regx occ = sy-index )."返回匹配到的串
index = index + 1.
WRITE AT index substr.
ENDDO.
1.7.2.FIND …SUBMATCHES
DATA: moff TYPE i,
mlen TYPE i,
s1 TYPE string,
s2 TYPE string,
s3 TYPE string,
s4 TYPE string.
FIND ALL OCCURRENCES OF REGEX `((\w+)\W+\2\W+(\w+)\W+\3)`"\2 \3 表示反向引用前面匹配到的第二与第三个子串
IN `Hey hey, my my, Rock and roll can never die Hey hey, my my`"会匹配二次,但只会返回第二次匹配到的结果,第一次匹配到的子串不会存储到s1、s2、s3中去
IGNORING CASE
MATCH OFFSET moff
MATCH LENGTH mlen
SUBMATCHES s1 s2 s3 s4."根据从外到内,从左到右的括号顺序依次存储到s1 s2…中,注:只取出使用括号括起来的子串,如想取总体子串则也要括起来,这与Java不一样
WRITE: / s1, / s2,/ s3 ,/ s4,/ moff ,/ mlen."s4会被忽略
1.7.3. FIND …RESULTS itab
DATA: result TYPE STANDARD TABLE OF string WITH HEADER LINE .
"与Java不一样,只要是括号括起来的都称为子匹配(即便用总体也用括号括起来了),
"无论括号嵌套多少层,统称为子匹配,且匹配到的全部子串都会存储到,
"MATCH_RESULT-SUBMATCHES中,即便最外层的括号匹配到的子串也会存储到SUBMATCHES
"内表中。括号解析的顺序为:从外到内,从左到右的优先级顺序来解析匹配结构。
"Java中的group(0)存储的是总体匹配串,即便总体未(或使用)使用括号括起来
PERFORM get_match TABLES result
USING '2011092131221032' '(((\d{2})(\d{2}))(\d{2})(\d{2}))'.
LOOP AT result .
WRITE: / result.
ENDLOOP.
FORM get_match TABLES p_result"返回全部分组匹配(括号括起来的表达式)
USING p_str
p_reg.
DATA: result_tab TYPE match_result_tab WITH HEADER LINE.
DATA: subresult_tab TYPE submatch_result_tab WITH HEADER LINE.
"注意:带表头时 result_tab 后面必定要带上中括号,不然激活时出现奇怪的问题
FIND ALL OCCURRENCES OF REGEX p_reg IN p_str RESULTS result_tab[].
"result_tab中存储了匹配到的子串自己(与Regex总体匹配的串,存储在
"result_tab-offset、result_tab-length中)以及所子分组(括号部分,存储在
"result_tab-submatches中)
LOOP AT result_tab .
"如需取总体匹配到的子串(与Regex总体匹配的串),则使用括号将总体Regex括起来
"来便可,括起来后也会自动存储到result_tab-submatches,而不须要在这里像这样读取
* p_result = p_str+result_tab-offset(result_tab-length).
* APPEND p_result.
subresult_tab[] = result_tab-submatches.
LOOP AT subresult_tab.
p_result = p_str+subresult_tab-offset(subresult_tab-length).
APPEND p_result.
ENDLOOP.
ENDLOOP.
ENDFORM.
1.7.4.正则式类
regex = Regular expression [ˈreɡjulə]
cl_abap_regex:与Java中的 java.util.regex.Pattern的类对应
cl_abap_matcher:与Java中的 java.util.regex.Matcher的类对应
1.7.4.1.matches、match
是否彻底匹配(正则式中没必要使用 ^ 与 $);matches为静态方法,而match为实例方法,做用都是同样
DATA: matcher TYPE REF TO cl_abap_matcher,
match TYPE match_result,
match_line TYPE submatch_result.
"^$能够省略,由于matches方法自己就是彻底匹配整个Regex
IF cl_abap_matcher=>matches( pattern = '^(db(ai).*)$' text = 'dbaiabd' ) = 'X'.
matcher = cl_abap_matcher=>get_object( )."获取最后一次匹配到的 Matcher 实例
match = matcher->get_match( ). "获取最近一次匹配的结果(注:是总体匹配的结果)
WRITE / matcher->text+match-offset(match-length).
LOOP AT match-submatches INTO match_line. "提取子分组(括号括起来的部分)
WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDIF.
DATA: matcher TYPE REF TO cl_abap_matcher,
match TYPE match_result,
match_line TYPE submatch_result.
"^$能够省略,由于matche方法自己就是彻底匹配整个Regex
matcher = cl_abap_matcher=>create( pattern = '^(db(ai).*)$' text = 'dbaiabd' ).
IF matcher->match( ) = 'X'.
match = matcher->get_match( ). "获取最近一次匹配的结果
WRITE / matcher->text+match-offset(match-length).
LOOP AT match-submatches INTO match_line. "提取子分组(括号括起来的部分)
WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDIF.
1.7.4.2.contains
是否包含(也可在正则式中使用 ^ 与 $ 用于彻底匹配检查,或者使用 ^ 检查是否匹配开头,或者使用 $ 匹配结尾)
DATA: matcher TYPE REF TO cl_abap_matcher,
match TYPE match_result,
match_line TYPE submatch_result.
IF cl_abap_matcher=>contains( pattern = '(db(ai).{2}b)' text = 'dbaiabddbaiabb' ) = 'X'.
matcher = cl_abap_matcher=>get_object( ). "获取最后一次匹配到的 Matcher 实例
match = matcher->get_match( ). "获取最近一次匹配的结果
WRITE / matcher->text+match-offset(match-length).
LOOP AT match-submatches INTO match_line. "提取子分组(括号括起来的部分)
WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDIF.
1.7.4.3.find_all
DATA: matcher TYPE REF TO cl_abap_matcher,
match_line TYPE submatch_result,
itab TYPE match_result_tab WITH HEADER LINE.
matcher = cl_abap_matcher=>create( pattern = '<[^<>]*(ml)>' text = '<html>hello</html>' )."建立 matcher 实例
"注:子分组存储在itab-submatches字段里
itab[] = matcher->find_all( ).
LOOP AT itab .
WRITE: / matcher->text, itab-offset, itab-length,matcher->text+itab-offset(itab-length).
LOOP AT itab-submatches INTO match_line. "提取子分组(括号括起来的部分)
WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDLOOP.
1.7.4.4.find_next
逐个找出匹配的子串,包括子分组(括号括起的部分)
DATA: matcher TYPE REF TO cl_abap_matcher,
match TYPE match_result, match_line TYPE submatch_result,
itab TYPE match_result_tab WITH HEADER LINE.
matcher = cl_abap_matcher=>create( pattern = '<[^<>]*(ml)>' text = '<html>hello</html>' ).
WHILE matcher->find_next( ) = 'X'.
match = matcher->get_match( )."获取最近一次匹配的结果
WRITE: / matcher->text, match-offset, match-length,matcher->text+match-offset(match-length).
LOOP AT match-submatches INTO match_line. "提取子分组(括号括起来的部分)
WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDWHILE.
1.7.4.5.get_length、get_offset、get_submatch
DATA: matcher TYPE REF TO cl_abap_matcher,
length TYPE i,offset TYPE i,
submatch TYPE string.
matcher = cl_abap_matcher=>create( pattern = '(<[^<>]*(ml)>)' text = '<html>hello</html>' ).
WHILE matcher->find_next( ) = 'X'. "循环2次
"为0时,表示取整个Regex匹配到的子串,这与Java同样,但若是整个Regex使用括号括起来后,
"则分组索引为1,这又与Java不同(Java无论是否使用括号将整个Regex括起来,分组索引号都为0)
"上面Regex中共有两个子分组,再加上整个Regex为隐含分组,因此一共为3组
DO 3 TIMES.
"在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串长度
length = matcher->get_length( sy-index - 1 ).
"在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串起始位置
offset = matcher->get_offset( sy-index - 1 ).
"在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串
submatch = matcher->get_submatch( sy-index - 1 ).
WRITE:/ length , offset,matcher->text+offset(length),submatch.
ENDDO.
SKIP.
ENDWHILE.
1.7.4.6.replace_all
DATA: matcher TYPE REF TO cl_abap_matcher,
count TYPE i,
repstr TYPE string.
matcher = cl_abap_matcher=>create( pattern = '<[^<>]*>' text = '<html>hello</html>' ).
count = matcher->replace_all( ``)."返回替换的次数
repstr = matcher->text. "获取被替换后的新串
WRITE: / count , repstr.
1.8.CLEAR、REFRESH、FREE
内表:若是使用有表头行的内表,CLEAR 仅清除表格工做区域。要重置整个内表而不清除表格工做区域,使用REFRESH语句或 CLEAR 语句CLEAR <itab>[].;REFRESH加不加中括号都是只清内表,另外REFRESH是专为清内表的,不能清基本类型变量,但CLEAR能够
以上都不会释放掉内表所占用的空间,若是想初始化内表的同时还要释放所占用的空间,请使用:FREE <itab>.
1.9.ABAP程序中的局部与全局变量
报表程序中选择屏幕事件块(AT SELECTION-SCREEN)与逻辑数据库事件块、以及methods(类中的方法)、subroutines(FORM子过程)、function modules(Function函数)中声明的变量为局部的,即在这些块里声明的变量不能在其余块里使用,但这些局部变量能够覆盖同名的全局变量;除这些处理块外,其余块里声明的变量都属于全局的(如报表事件块、列表事件块、对话Module),效果与在程序最开头定义的变量效果是同样的,因此能够在其余处理块直接使用(但要注意的是,需遵照先定义后使用的原则,这种前后关系是从语句书写顺序来讲的,与事件块的自己运行顺序没有关系);另外,局部变量声明时,无论在处理块的任何地方,其效果都是至关于处理块里的全局变量,而不像其余语言如Java那样:局部变量的做用域能够存在于任何花括号{}之间(这就意味着局部变量在处理过程范围内是全局的),以下面的i,在ABAP语言中仍是会累加输出,而不会永远是1(在Java语言中会是1):
FORM aa.
DO 10 TIMES.
DATA: i TYPE i VALUE 0.
i = i + 1.
WRITE: / i.
ENDDO.
ENDFORM.
1.10.Form、Function
Form、Function中的TABLES参数,TYPE与LIKE后面只能接标准内表类型或标准内表对象,若是要使用排序内表或者哈希内表,则只能使用USING(Form)与CHANGING方式来代替。当把一个带表头的实参经过TABLES参数传递时,表头也会传递过去,若是实参不带表头或者只传递了表体(使用了[]时),系统会自动为内表参数变量建立一个局部空的表头
无论是以TABLES仍是以USING(Form)非值、CHANGE非值方式传递时,都是以引用方式(即别名,不是指地址,注意与Java中的传引用区别:Java实为传值,但传递的值为地址的值,而ABAP中传递的是否为地址,则要看实参是不是经过Type ref to定义的)传递;但若是USING值传递,则对形参数的修改不会改变实参,由于此时不是引用传递;但若是CHANGE值传递,对形参数的修改仍是会改变实参,只是修改的时机在Form执行或Function执行完后,才去修改
Form中经过引用传递时,USING与CHANGING彻底同样;但CHANGING为值传递方式时,须要在Form执行完后,才去真正修改实参变量的内容,因此CHANGING传值与传引用其结果都是同样:结果都修改了实参内容,只是修改的时机不太同样而已
1.10.1.FORM
FORM subr [TABLES t1 [{TYPE itab_type}|{LIKE itab}|{STRUCTURE struc}]
t2 […]]
[USING { VALUE(p1)|p1 } [ { TYPE generic_type }
| { LIKE <generic_fs>|generic_para }
| { TYPE {[LINE OF] complete_type}|{REF TO type} }
| { LIKE {[LINE OF] dobj} | {REF TO dobj} }
| STRUCTURE struc]
{ VALUE(p2)|p2 } […]]
[CHANGING{ VALUE(p1)|p1 } [ { TYPE generic_type }
| { LIKE <generic_fs>|generic_para }
| { TYPE {[LINE OF] complete_type} | {REF TO type} }
| { LIKE {[LINE OF] dobj} | {REF TO dobj} }
| STRUCTURE struc]
{ VALUE(p2)|p2 } […]]
[RAISING {exc1|RESUMABLE(exc1)} {exc2|RESUMABLE(exc2)} ...].
generic_type:为通用类型
complete_type:为彻底限制类型
<generic_fs>:为字段符号变量类型,以下面的 fs 形式参数
generic_para:为另外一个形式参数类型,以下面的 b 形式参数
DATA: d(10) VALUE'11'.
FIELD-SYMBOLS: <fs> LIKE d.
ASSIGN d TO <fs>.
PERFORM aa USING <fs> d d.
FORM aa USING fs like <fs> a like d b like a.
WRITE:fs,/ a , / b.
ENDFORM.
若是没有给形式参数指定类,则为ANY类型
若是TABLES与USING、CHANGING一块儿使用时,则必定要按照TABLES、USING、CHANGING顺序声明
值传递中的VALUE关键字只是在FORM定义时出现,在调用时PERFORM语句中无需出现,也就是说,调用时值传递和引用传递不存在语法格式差异
DATA : i TYPE i VALUE 100.
WRITE: / 'frm_ref===='.
PERFORM frm_ref USING i .
WRITE: / i."200
WRITE: / 'frm_val===='.
i = 100.
PERFORM frm_val USING i .
WRITE: / i."100
WRITE: / 'frm_ref2===='.
"不能将下面的变量定义到frm_ref2过程当中,若是这样,下面的dref指针在调用frm_ref2 后,指向的是Form中局部变量内存,为不安全发布,运行会抛异常,由于From结束后,它所拥有的全部变量内存空间会释放掉
DATA: i_frm_ref2 TYPE i VALUE 400.
i = 100.
DATA: dref TYPE REF TO i .
get REFERENCE OF i INTO dref.
PERFORM frm_ref2 USING dref ."传递的内容为地址,属于别名引用传递
WRITE: / i."4000
field-SYMBOLS : <fs> TYPE i .
ASSIGN dref->* to <fs>."因为frm_ref2过程当中已修改了dref的指向,现指向了i_frm_ref2 变量的内存空间
WRITE: / <fs>."400
WRITE: / 'frm_val2===='.
i = 100.
DATA: dref2 TYPE REF TO i .
get REFERENCE OF i INTO dref2.
PERFORM frm_val2 USING dref2 .
WRITE: / i."4000
ASSIGN dref2->* to <fs>.
WRITE: / <fs>."4000
FORM frm_ref USING p_i TYPE i ."C++中的引用参数传递:p_i为实参i的别名
WRITE: / p_i."100
p_i = 200."p_i为参数i的别名,因此能够直接修改实参
ENDFORM.
FORM frm_val USING value(p_i)."传值:p_i为实参i的拷贝
WRITE: / p_i."100
p_i = 300."因为是传值,因此不会修改主调程序中的实参的值
ENDFORM.
FORM frm_ref2 USING p_i TYPE REF TO i ."p_i为实参dref的别名,相似C++中的引用参数传递(传递的内容为地址,而且属于别名引用传递)
field-SYMBOLS : <fs> TYPE i .
"如今<fs>就是实参所指向的内存内容的别名,表明实参所指向的实际内容
ASSIGN p_i->* to <fs>.
WRITE: / <fs>."100
<fs> = 4000."直接修改实参所指向的实际内存
DATA: dref TYPE REF TO i .
get REFERENCE OF i_frm_ref2 INTO dref.
"因为USING为C++的引用参数,因此这里修改的直接是实参所存储的地址内容,这里的p_i为传进来的dref的别名,是同一个变量,因此实参的指向也发生了改变(这与Java中传递引用是不同的,Java中传递引用时为地址的拷贝,即Java中永远也只有传值,但C/C++/ABAP中能够传递真正引用——别名)
p_i = dref."此处会修改实参的指向
ENDFORM.
FORM frm_val2 USING VALUE(p_i) TYPE REF TO i ."p_i为实参dref2的拷贝,相似Java中的引用传递(虽然传递的内容为地址,但传递的方式属于地址拷贝——值传递)
field-SYMBOLS : <fs> TYPE i .
"如今<fs>就是实参所指向的内存内容的别名,表明实参所指向的实际内容
ASSIGN p_i->* to <fs>.
WRITE: / <fs>."100
<fs> = 4000."但这里仍是能够直接修改实参所指向的实际内容
DATA: dref TYPE REF TO i .
get REFERENCE OF i_frm_ref2 INTO dref.
"这里与过程 frm_ref2 不同,该过程 frm_val2 参数的传递方式与java中的引用传递是原理是同样的:传递的是地址拷贝,因此下面不会修改主调程序中实参dref2的指向,它所改变的只是拷贝过来的Form中局部形式参数的指向
p_i = dref.
ENDFORM.
1.10.2.FUNCTION
1.10.2.1. Function Group结构
当使用Function Builder建立函数组时,系统会自动建立main program与相应的include程序:
l<fgrp>为Function Group的名称
lSAPL<fgrp>为主程序名,它将Function Group里的全部Include文件包括进来,除了INCLUDE语句以外,没有其余语句了
lL<fgrp>TOP,里面有FUNCTION-POOL语句,以及全部Function Module均可以使用的全局数据定义
lL<fgrp>UXX,也只有INCLUDE语句,它所包括的Include文件为相应具体Function Module所对应Include文件名:L<fgrp>U01、L<fgrp>U02、...这些Include文件实际上包含了所对应的Function Module代码(即双击它们进去就是对应的Function,而显示的不是真正Include文件所对应的代码)
lL<fgrp>U01和L<fgrp>U02中的01、02编号对应L<fgrp>UXX中的“XX”,表明其建立前后的序号,例如L<fgrp>U01和L<fgrp>U02是头两个被建立的函数,在函数组中建立出的函数代码就放在相应的L<fgrp>UXX(这里的XX表明某个数字,而不是字面上的XX)Include头文件中
lL<fgrg>FXX,用来存一些Form子过程,而且能够被全部的Function Modules所使用(不是针对某个Function Module的,但通常在设计时会针对每一个Function Module设计这样单独的Include文件,这是一个好习惯),而且在使用时不须要在Function Module中使用INCLUDE语句包含它们(由于这些文件在主程序SAPL<fgrp>里就已经被Include进来了)。另外,L<fgrg>FXX中的F是指Form的意思,这是一种名称约束而已,在建立时咱们能够随便指定,通常还有IXX(表示些类Include文件包括的是一些PAI事件中调用的Module,有时干脆直接使用L<fgrg>PAI或者L<fgrg>PAIXX),OXX(表示些类Include文件包括的是一些PBO事件中调用的Module,有时干脆直接使用L<fgrg>PBO或者L<fgrg>PBOXX)。注:若是Form只被某一函数单独使用,实质上还可直接将这些Form定义在Function Module里的ENDFUNCTION语句后面
当你调用一个function module时,系统加将整个function group(包括Function Module、Include文件等)加载到主调程序所在的internal session中,而后该Function Module获得执行,该Function Group一直保留在内存中,直到internal session结束。Function Group中的所定义的Include文件中的变量是全局,被全部Function Module共享,因此Function Group比如Java中的类,而Function Module则比如类中的方法,因此Function Group中的Include文件中定义的东西是全局型的,能被全部Function Module所共享使用
1.10.2.2.Function参数传值、传址
function fuc_ref .
*"-------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" REFERENCE(I_I1) TYPE IREFERENCE(别名)为参数的默认传递类型
*" VALUE(I_I2) TYPE I 定义时勾选了Pass Value选项才会是 VALUE类型
*" REFERENCE(I_I3) TYPE REF TO I
*" VALUE(I_I4) TYPE REF TO I
*" EXPORTING
*" REFERENCE(E_I1) TYPE I
*" VALUE(E_I2) TYPE I
*" REFERENCE(E_I3) TYPE REF TO I
*" VALUE(E_I4) TYPE REF TO I
*" TABLES
*" T_1 TYPE ZJZJ_ITAB
*" CHANGING
*" REFERENCE(C_I1) TYPE I
*" VALUE(C_I2) TYPE I
*" REFERENCE(C_I3) TYPE REF TO I
*" VALUE(C_I4) TYPE REF TO I
*"-------------------------------------------------------------------
write: / i_i1."1
"因为i_i1为输入类型参数,且又是引用类型,实参不能被修改。这里i_i1是以C++中的引用(别名)参数方式传递参数,因此若是修改了i_i1就会修改实际参数,因此函数中不能修改REFERENCE 的 IMPORTING类型的参数,若是去掉下面注释则编译出错
"i_i1 = 10.
write: / i_i2."2
"虽然i_i2是输入类型的参数,但不是引用类型,因此能够修改,编译能经过但不会修改外面实参的值,只是修改了该函数局部变量的值
i_i2 = 20.
field-symbols: <fs> type i .
assign i_i3->* to <fs>.
"因为i_i3存储的是地址,因此先要解引用再能使用
write: / <fs>.
"同上面,REFERENCE的IMPORTING类型的参数不能被修改:这里即不能修改实参的指向"GET REFERENCE OF 30 INTO i_i3."虽然不能够修改实参的指向,但能够修改实参所指向的实际内容
<fs> = 30.
assign i_i4->* to <fs>.
"i_i4存储也的是地址,因此先要解引用再能使用
write: / <fs>.
"虽然i_i4是输入类型的参数,但不是引用类型,因此能够修改,只会修改函数中的局部参数i_i4的指向,但并不会修改实参的指向
get reference of 40 into i_i4.
"虽然不能修改实参的指向,但能够直接修改实参的所指向的实际内容
<fs> = 400.
WRITE: / c_i1."111
"c_i1为实参的别名,修改形参就等于修改实参内容
c_i1 = 1110.
WRITE: / c_i2."222
"c_i2为实参的副本,因此不会影响实参的内容,可是,因为是CHANGING类型的参数,且为值传递,在函数正常执行完后,仍是会将该副本再次拷贝给实参,因此最终实参仍是会被修改
c_i2 = 2220.
ENDFUNCTION.
调用程序:
DATA: i_i1 TYPE i VALUE 1,
i_i2 TYPE i VALUE 2,
i_i3 TYPE REF TO i ,
i_i4 TYPE REF TO i ,
c_i1 TYPE i VALUE 111,
c_i2 TYPE i VALUE 222,
c_i3 TYPE REF TO i ,
c_i4 TYPE REF TO i ,
t_1 TYPE zjzj_itab WITH HEADER LINE.
DATA: i_i3_ TYPE i VALUE 3.
GET REFERENCE OF i_i3_ INTO i_i3.
DATA: i_i4_ TYPE i VALUE 4.
GET REFERENCE OF i_i4_ INTO i_i4.
DATA: c_i3_ TYPE i VALUE 333.
GET REFERENCE OF c_i3_ INTO c_i3.
DATA: c_i4_ TYPE i VALUE 444.
GET REFERENCE OF c_i4_ INTO c_i4.
CALL FUNCTION 'FUC_REF'
EXPORTING
i_i1 = i_i1
i_i2 = i_i2
i_i3 = i_i3
i_i4 = i_i4
TABLES
t_1 = t_1
CHANGING
c_i1 = c_i1
c_i2 = c_i2
c_i3 = c_i3
c_i4 = c_i4.
WRITE: / i_i2."2
WRITE: / i_i3_."30
WRITE: / i_i4_."400
WRITE: / c_i1."1110
WRITE: / c_i2."2220
1.11. 字段符号FIELD-SYMBOLS
字段符号能够看做仅是已经被解引用的指针(相似于C语言中带有解引用操做符 * 的指针),但更像是C++中的引用类型(int i ;&ii= i;),即某个变量的别名,它与真正的指针仍是有很大的区别的,在ABAP中引用变量(经过TYPE REF TO定义的变量)才比如C语言中的指针
ASSIGN ... TO <fs>:将某个内存区域分配给字段符号,这样字段符号就表明了该内存区域,即该内存区域别名
1.11.1.ASSIGN隐式强转
TYPES: BEGIN OF t_date,
year(4) TYPE n,
month(2) TYPE n,
day(2) TYPE n,
END OF t_date.
FIELD-SYMBOLS <fs> TYPE t_date."将<fs>定义成了具体限定类型
ASSIGN sy-datum TO <fs> CASTING. "后面没有指定具体类型,因此使用定义时的类型进行隐式转换
1.11.2.ASSIGN显示强转
DATA txt(8) TYPE c VALUE '19980606'.
FIELD-SYMBOLS <fs>.
ASSIGN txt TO <fs> CASTING TYPE d."因为定义时未指定具体的类型,因此这里须要显示强转
1.11.3.ASSIGN 动态分配
1.11.4.UNASSIGN、CLEAR
UNASSIGN:该语句是初始化<FS>字段符号,执行后字段符号将再也不引用内存区域,<fs> is assigned返回假
CLEAR:与UNASSIGN不一样的是,只有一个做用就是初始化它所指向的内存区域,而不是解除分配
1.12.数据引用、对象引用
TYPE REF TO data 数据引用data references
TYPE REF TO object 对象引用object references
除了object,全部的通用类型都能直接用TYPE后面(如TYPE data,但没有TYPE object,object不能直接跟在TYPE后面,只能跟在TYPE REF TO后面)
TYPE REF TO 后面可接的通用类型只能是data(数据引用)或者是object(对象引用)通用类型,其余通用类型不行
1.12.1.数据引用Data References
DATA: dref TYPE REF TO i ."dref即为数据引用,即数据指针,指向某个变量或常量,存储变量地址
CREATE DATA dref.
dref->* = 2147483647."可直接解引用使用,不须要先经过分配给字段符号后再使用
DATA: BEGIN OF strct,
c,
END OF strct.
DATA: dref LIKE REF TO strct .
CREATE DATA dref .
dref->*-c = 'A'.
TYPES: tpy TYPE c.
DATA: c1 TYPE REF TO tpy.
DATA: c2 LIKE REF TO c1."二级指针
GET REFERENCE OF 'a' INTO c1.
GET REFERENCE OF c1 INTO c2.
WRITE: c2->*->*."a
1.12.2.对象引用Object references
CLASS cl DEFINITION.
PUBLIC SECTION.
DATA: i VALUE 1.
ENDCLASS.
CLASS cl IMPLEMENTATION.
ENDCLASS.
DATA: obj TYPE REF TO cl.
CREATE OBJECT obj. "建立对象
DATA: oref LIKE REF TO obj. "oref即为对象引用,即对象指针,指向某个对象,存储对象地址
GET REFERENCE OF obj INTO oref. "获取对象地址
WRITE: oref->*->i."1
1.12.3.GET REFERENCE OF获取变量/对象/常量地址
DATA: e_i3 TYPE REF TO i .
GET REFERENCE OF 33 INTO e_i3.
WRITE: e_i3->*."33
"但不能修改常量的值
"e_i3->* = 44.
DATA: i TYPE i VALUE 33,
dref LIKE REF TO i."存储普通变量的地址
GET REFERENCE OF i INTO dref.
dref->* = 44.
WRITE: i. "44
1.13.动态语句
1.13.1.内表动态访问
SORT itab BY (comp1)...(compn)
READ TABLE itab WITH KEY(k1)=v1...(kn)=vn
READ TABLE itab...INTOwaCOMPARING(comp1)...(compn) TRANSPORTING(comp1)...
MODIFY [TABLE] itab TRANSPORTING(comp1)...(compn)
DELETE TABLEitabWITH TABLE KEY(comp1)...(compn)
DELETE ADJACENT DUPLICATES FROM itab COMPARING(comp1)...(compn)
AT NEW/END OF (comp)
1.13.2.动态类型
CREATE DATA ... TYPE (type)...
DATA: a TYPE REF TO i.
CREATE DATA a TYPE ('I').
a->* = 1.
CREATE OBJECT ... TYPE (type)...请参考类对象反射章节
1.13.3.动态SQL
MODIFY/UPDATE(dbtab)...
1.13.4.动态调用类的方法
CALL METHOD (meth_name)
| cref->(meth_name)
| iref->(meth_name)
| (class_name)=>(meth_name)
| class=>(meth_name)
| (class_name)=>meth
实例请参考类对象反射章节
1.13.5.ASSIGN 动态分配
FIELD-SYMBOLS:<fs>.
DATA:str(20) TYPE c VALUE 'Output String',
name(20) TYPE c VALUE 'STR'.
"静态分配:编译时就知道要分配的对象名
ASSIGN name TO <fs>."结果是<fs>与name变量等同
"经过变量名动态访问变量
ASSIGN (name) TO <fs>."结果是是<fs>的值为str变量值
DATA: BEGIN OF line,
col1 TYPE i VALUE '11',
col2 TYPE i VALUE '22',
col3 TYPE i VALUE '33',
END OF line.
DATA comp(5) VALUE 'COL3'.
FIELD-SYMBOLS: <f1>, <f2>, <f3>.
ASSIGN line TO <f1>.
ASSIGN comp TO <f2>.
"还可以直接使用如下的语法访问其余程序中的变量
ASSIGN ('(ZJDEMO)SBOOK-FLDATE') TO <fs>.
"经过索引动态的访问结构成员
ASSIGN COMPONENT sy-index OF STRUCTURE <f1> TO <f3>.
"经过字段名动态的访问结构成员
ASSIGN COMPONENT <f2>OF STRUCTURE <f1> TO <f3>.
"若是定义的内表没有组件名时,能够使用索引为0的组件来访问这个无名字段(注:不是1)
ASSIGN COMPONENT 0 OF STRUCTURE itab TO <fs>.
1.13.5.1.动态访问类的属性成员
ASSIGN oref->('attr') TO <attr>.
ASSIGN oref->('static_attr') TO <attr>.
ASSIGN ('C1')=>('static_attr') TO <attr>.
ASSIGN c1=>('static_attr') TO <attr>.
ASSIGN ('C1')=>static_attr TO <attr>.
实例请参考类对象反射章节
1.14.反射
|--CL_ABAP_DATADESCR
| |--CL_ABAP_REFDESCR
| |--CL_ABAP_COMPLEXDESCR
|--CL_ABAP_INTFDESCR
DATA: structtype TYPE REF TO cl_abap_structdescr.
structtype ?= cl_abap_typedescr=>describe_by_name( 'spfli' ).
*COMPDESC-TYPE ?= CL_ABAP_DATADESCR=>DESCRIBE_BY_NAME( 'EKPO-MATNR' ).
DATA: datatype TYPE REF TO cl_abap_datadescr,
field(5) TYPE c.
datatype ?= cl_abap_typedescr=>describe_by_data( field ).
DATA: elemtype TYPE REF TO cl_abap_elemdescr.
elemtype = cl_abap_elemdescr=>get_i( ).
elemtype = cl_abap_elemdescr=>get_c( 20 ).
DATA: oref1 TYPE REF TO object.
DATA: descr_ref1 TYPE REF TO cl_abap_typedescr.
CREATE OBJECT oref1 TYPE ('C1'). "C1为类名
descr_ref1 = cl_abap_typedescr=>describe_by_object_ref( oref1 ).
还有一种:describe_by_data_ref
1.14.1.TYPE HANDLE
handle只能是CL_ABAP_DATADESCR或其子类的引用变量,注:只能用于Data类型,不能用于Object类型,即不能用于CL_ABAP_ OBJECTDESCR,因此没有:
CREATE OBJECT dref TYPE HANDLE objectDescr.
DATA: dref TYPE REF TO data,
c10type TYPE REF TO cl_abap_elemdescr.
c10type = cl_abap_elemdescr=>get_c( 10 ).
CREATE DATA dref TYPE HANDLE c10type.
DATA: x20type TYPE REF TO cl_abap_elemdescr.
x20type = cl_abap_elemdescr=>get_x( 20 ).
FIELD-SYMBOLS: <fs> TYPE any.
ASSIGN dref->* TO <fs> CASTING TYPE HANDLE x20type.
1.14.2.动态建立数据Data或对象Object
TYPES: ty_i TYPE i.
DATA: dref TYPE REF TO ty_i .
CREATE DATA dref TYPE ('I')."根据基本类型名动态建立数据
dref->* = 1.
WRITE: / dref->*." 1
CREATE OBJECT oref TYPE ('C1')."根据类名动态建立实例对象
1.14.3.动态建立基本类型变量、结构、内表
DATA: dref_str TYPE REF TO data,
dref_tab TYPE REF TO data,
dref_i TYPE REF TO data,
itab_type TYPE REF TO cl_abap_tabledescr,
struct_type TYPE REF TO cl_abap_structdescr,
elem_type TYPE REF TO cl_abap_elemdescr,
table_type TYPE REF TO cl_abap_tabledescr,
comp_tab TYPE cl_abap_structdescr=>component_table WITH HEADER LINE.
FIELD-SYMBOLS :<fs_itab> TYPE ANY TABLE.
**=========动态建立基本类型
elem_type ?= cl_abap_elemdescr=>get_i( ).
CREATE DATA dref_i TYPE HANDLE elem_type ."动态的建立基本类型数据对象
**=========动态建立结构类型
struct_type ?= cl_abap_typedescr=>describe_by_name( 'SFLIGHT' )."结构类型
comp_tab[] = struct_type->get_components( )."组成结构体的各个字段组件
* 向结构中动态的新增一个成员
comp_tab-name = 'L_COUNT'."为结构新增一个成员
comp_tab-type = elem_type."新增成员的类型对象
INSERT comp_tab INTO comp_tab INDEX 1.
* 动态建立结构类型对象
struct_type = cl_abap_structdescr=>create( comp_tab[] ).
CREATE DATA dref_str TYPE HANDLE struct_type."使用结构类型对象来建立结构对象
**=========动态建立内表
* 基于结构类型对象建立内表类型对象
itab_type = cl_abap_tabledescr=>create( struct_type ).
CREATE DATA dref_tab TYPE HANDLE itab_type."使用内表类型对象来建立内表类型
ASSIGN dref_tab->* TO <fs_itab>."将字段符号指向新建立出来的内表对象
"**========给现有的内表动态的加一列
table_type ?= cl_abap_tabledescr=>describe_by_data( itab ).
struct_type ?= table_type->get_table_line_type( ).
comp_tab[] = struct_type->get_components( ).
comp_tab-name = 'FIDESC'.
comp_tab-type = cl_abap_elemdescr=>get_c( 120 ).
INSERT comp_tab INTO comp_tab INDEX 2.
struct_type = cl_abap_structdescr=>create( comp_tab[] ).
itab_type = cl_abap_tabledescr=>create( struct_type ).
CREATE DATA dref_tab TYPE HANDLE itab_type.
1.14.4.类对象反射
CLASS c1 DEFINITION.
PUBLIC SECTION.
DATA: c VALUE 'C'.
METHODS: test.
ENDCLASS.
CLASS c1 IMPLEMENTATION.
METHOD:test.
WRITE:/ 'test'.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
TYPES: ty_c.
DATA: oref TYPE REF TO object .
DATA: oref_classdescr TYPE REF TO cl_abap_classdescr .
CREATE OBJECT oref TYPE ('C1')."根据类名动态建立实例对象
"至关于Java中的Class类对象
oref_classdescr ?= cl_abap_classdescr=>describe_by_object_ref( oref ).
DATA: t_attrdescr_tab TYPE abap_attrdescr_tab WITH HEADER LINE,"类中的属性列表
t_methdescr_tab TYPE abap_methdescr_tab WITH HEADER LINE."类中的方法列表
FIELD-SYMBOLS <fs_attr> TYPE any.
t_attrdescr_tab[] = oref_classdescr->attributes.
t_methdescr_tab[] = oref_classdescr->methods.
LOOP AT t_attrdescr_tab."动态访问类中的属性
ASSIGN oref->(t_attrdescr_tab-name) TO <fs_attr>.
WRITE: / <fs_attr>.
ENDLOOP.
LOOP AT t_methdescr_tab."动态访问类中的方法
CALL METHOD oref->(t_methdescr_tab-name).
ENDLOOP.
2.面向对象
2.1. 类与接口定义
CLASS class DEFINITION [ABSTRACT][FINAL].
[PUBLIC SECTION.
[components]]
[PROTECTED SECTION.
[components]]
[PRIVATE SECTION.
[components]]
ENDCLASS.
INTERFACE intf.
[components]
ENDINTERFACE.
2.1.1. components
²TYPES, DATA, CLASS-DATA, CONSTANTS for data types and data objects
²METHODS, CLASS-METHODS, EVENTS, CLASS-EVENTS for methods and events
² INTERFACES(若是在类中,表示须要实现哪一个接口;若是是在接口中,表示继承哪一个接口) for implementing interfaces
² ALIASESfor alias names for interface components给接口组件取别名
2.2.类定义、实现
CLASS math DEFINITION.
PUBLIC SECTION.
METHODS divide_1_by
IMPORTING operand TYPE i
EXPORTING result TYPE f
RAISING cx_sy_arithmetic_error.
ENDCLASS.
CLASS math IMPLEMENTATION.
METHOD divide_1_by.
result = 1 / operand.
ENDMETHOD.
ENDCLASS.
2.3.接口定义、实现
INTERFACEint1.
ENDINTERFACE.
CLASSclass DEFINITION. [ˌdefiˈniʃən]
PUBLICSECTION.
INTERFACES: int1,int2."可实现多个接口
ENDCLASS.
CLASS class IMPLEMENTATION. [ˌɪmplɪmənˈteɪ-ʃən]
METHOD intf1~imeth1.
ENDMETHOD.
ENDCLASS.
2.4.类、接口继承
CLASS<subclass> DEFINITIONINHERITINGFROM<superclass>.
INTERFACE i0.
METHODS m0.
ENDINTERFACE.
INTERFACE i1.
INTERFACES i0.
"能够有相同的成员名,由于继承过来后,成员仍是具备各自的命名空间,在实现时
"被继承过来的叫 i0~m0,在这里的名为i1~m0,因此是不一样的两个方法
METHODS m0.
METHODS m1.
ENDINTERFACE.
2.5. 向下强转型 ?=
CLASS person DEFINITION.
ENDCLASS.
CLASS stud DEFINITION INHERITING FROMperson.
ENDCLASS.
START-OF-SELECTION.
DATA p TYPE REF TO person.
DATA s TYPE REF TO stud.
CREATE OBJECT s.
p = s. "向上自动转型
"拿开注释运行时抛异常,由于P此时指向的对象不是Student,而是Person因此能强转的前提是P指向的是Student
"CREATE OBJECT p.
2.6. 方法
METHODS/CLASS-METHODS meth [ABSTRACT|FINAL]
[IMPORTING parameters [PREFERRED PARAMETER p]]
[EXPORTING parameters]
[CHANGING parameters]
[{RAISING|EXCEPTIONS} exc1 exc2 ...].
应该还有一个Returning选项,且RETURNING不能与EXPORTING、CHANGING同时使用:
2.6.1.parameters
... { VALUE(p1) | REFERENCE(p1) | p1 }
{ TYPE generic_type }
|{TYPE{[LINE OF] complete_type}|{REF TO {data|object|complete_type |class|intf}}}
|{LIKE{[LINE OF] dobj}|{REF TO dobj} }
[OPTIONAL|{DEFAULT def1}]
{ VALUE(p2) | REFERENCE(p2) | p2 }...
²data、object:表示是通用数据类型data、object
²complete_type:为彻底限定类型
²OPTIONAL与DEFAULT两个选项不能同时使用,且对于EXPORTING类型的输入参数不能使用
²若是参数名p1前没有使用VALUE、REFERENCE,则默认为仍是REFERENCE,即引用传递
²方法中的输入输出参数是否能修改,请参考Form、Function参数的传值传址
2.6.2. PREFERRED PARAMETER首选参数
设置多个IMPORTING类型参数中的某一个参数为首选参数。
首选参数的意义在于:当全部IMPORTING类型都为可选optional时,咱们能够经过PREFERRED PARAMETER选项来指定某一个可选输入参数为首选参数,则在如下简单方式调用时:[CALL METHOD] meth( a ). 实参a的值就会传递给设置的首选参数,而其余不是首参数的可选输入参数则留空或使用DEFAULT设置的默认值
注:此选项只能用于IMPORTING类型的参数;若是有必选的IMPORTING输入参数,则没有意义了
2.6.3. 普通调用
[CALL METHOD] meth|me->meth|oref->meth|super->meth|class=>meth[(]
[EXPORTING p1 = a1 p2 = a2 ...]
{ {[IMPORTING p1=a1 p2=a2 ...][CHANGING p1 = a1 p2 = a2 ...]}
|[RECEIVING r = a ] } RECEIVING不能与EXPORTING、CHANGING同时使用
[EXCEPTIONS [exc1 = n1 exc2 = n2 ...]
[OTHERS = n_others] ] [)].
若是省略CALL METHOD,则必定要加上括号形式;若是经过CALL METHOD来调用,则括号可加可不加
RECEIVING:用来接收METHODS /CLASS-METHODS 中RETURNING选项返回的值
若是EXPORTING、IMPORTING、CHANGING、RECEIVING、EXCEPTIONS、OTHERS同时出现时,应该按此顺序来编写
注:使用此种方式调用(使用 EXPORTING、IMPORTING等这些选项)时,若是原方法声明时带了返回值RETURNING,只能使用RECEIVING来接受,而不能使用等号来接收返回值,下面用法是错误的:
num2 = o1->m1( EXPORTING p1 = num1 ).
2.6.4. 简单调用
此方式下输入的参数都只能是IMPORTING类型的参数,若是要传CHANGING、EXPORTING、RAISING、EXCEPTIONS类型的参数时,只能使用上面通用调用方式。
²meth( )
此种方式仅适用于没有输入参数(IMPORTING)、输入\输出参数(CHANGING)、或者有但都是可选的、或者不是可选时但有默认值也可
²meth( a )
此种方式仅适用于只有一个必选输入参数(IMPORTING)(若是还有其余输入参数,则其余都为可选,或者不是可选时但有默认值也可),或者是有多个可选输入参数(IMPORTING)(此时没有必选输入参数状况下)的状况下但方法声明时经过使用PREFERRED PARAMETER选项指定了其中某个可选参数为首选参数(首选参数即在使用meth( a )方式传递一个参数进行调用时,经过实参a传递给设置为首选的参数)
²meth( p1 = a1 p2 = a2 ... )
此种方式适用于有多个必选的输入参数(IMPORTING)方法的调用(其它如CHANGING、EXPORTING没有,或者有但可选),若是输入参数(IMPORTING)为可选,则也能够没必要传
2.6.5.函数方法
Return惟一返回值
METHODS meth
[IMPORTING parameters [PREFERRED PARAMETER p]]
RETURNINGVALUE(r) typing
[{RAISING|EXCEPTIONS} exc1 exc2 ...].
RETURNING :用来替换EXPORTING、CHANGING,不能同时使用。定义了一个形式参数 r 来接收返回值,而且只能是值传递
具备惟一返回值的函数方法能够直接用在如下语句中:逻辑表达式(IF、ELSEIF、WHILE、CHECK、WAIT)、CASE、LOOP、算术表达式、赋值语句
函数方法能够采用上面简单调用方式来调用:meth( )、meth( a )、meth( p1 = a1 P2 = a2 ... )
ref->m( RECEIVING r = i ).
CALL METHOD ref->m( RECEIVING r = i ).
CALL METHOD ref->m RECEIVING r = i.
2.7.me、super
等效于Java中的 this、super
2.8. 事件
2.8.1. 事件定义
EVENTS|CLASS-EVENTS evt [EXPORTING VALUE(p1)
{ TYPE generic_type }
|{TYPE {[LINE OF] complete_type} |
{ REF TO {data|object|complete_type|class|intf}} }
| {LIKE{[LINE OF] dobj} | {REF TO dobj} }
[OPTIONAL|{DEFAULT def1}]
VALUE(p2) ...].
²data、object:表示是通用数据类型data、object
²complete_type:为彻底限定类型
² OPTIONAL与DEFAULT两个选项不能同时使用
²EXPORTING:定义了事件的输出参数,而且事件定义时只能有输出参数,且只能是传值
非静态事件声明中除了明确使用EXPORTING定义的输出外,每一个实例事件其实还有一个隐含的输出参数sender,它指向了事件源对象,当使用RAISE EVENT语句触发一个事件时,事件源的对象就会分配给这个sender引用,可是静态事件没有隐含参数sender
事件evt的定义也是在类或接口的定义部分进行定义的
非静态事件只能在非静态方法中触发,而不能在静态方法中触发;而静态事件便可在静态也可在非静态方法中进行触发,或者反过来讲:实例方法既可触发静态事件,也可触发非静态事件,但静态方法就只能触发静态事件
2.8.2. 事件触发
RAISE EVENT evt [EXPORTING p1 = a1 p2 = a2 ...].
该语句只能在定义evt事件的同一类或子类,或接口实现方法中进行调用
当实例事件触发时,若是在event handler事件处理器声明语句中指定了形式参数sender,则会自动接收事件源,但不能在RAISE EVENT …EXPORTING语句中明确指定,它会自动传递(若是是静态事件,则不会传递sender参数)
CLASS c1 DEFINITION.
PUBLIC SECTION.
EVENTS e1 EXPORTING value(p1) TYPE string value(p2) TYPE i OPTIONAL. "定义
METHODS m1.
ENDCLASS.
CLASS c1 IMPLEMENTATION.
METHOD m1.
RAISE EVENT e1 EXPORTING p1 = '...'."触发
ENDMETHOD.
ENDCLASS.
2.8.3.事件处理器Event Handler
静态或非静态事件处理方法均可以处理静态或非静态事件,与事件的静态与否没有直接的关系
INTERFACE window. "窗口事件接口
EVENTS: minimize EXPORTINGVALUE(status) TYPE i."最小化事件
ENDINTERFACE.
CLASS dialog_window DEFINITION. "窗口事件实现
PUBLIC SECTION.
INTERFACES window.
ENDCLASS.
INTERFACE window_handler. "窗口事件处理器接口
METHODS: minimize_window FOR EVENT window~minimize OF dialog_window
IMPORTING status sender. "事件处理器方法参数要与事件接口定义中的一致
ENDINTERFACE.
2.8.4. 注册事件处理器
实例事件处理器(方法)注册(注:被注册的方法只能是用来处理非静态事件的方法):
SET HANDLER handler1 handler2 ... FOR oref|{ALL INSTANCES}[ACTIVATION act].
静态事件处理器(方法)注册(注:被注册的方法只能是用来处理静态事件的方法):
SET HANDLER handler1 handler2 ... [ACTIVATION act].
oref:只将事件处理方法handler1 handler2注册到 oref 这一个事件源对象
ALL INSTANCES:将事件处理方法注册到全部的事件源实例中
ACTIVATION act:表示是注册仍是注销
2.8.5. 示例
CLASS c1 DEFINITION."事件源
PUBLIC SECTION.
EVENTS: e1 EXPORTING value(p1) TYPE c,e2.
CLASS-EVENTS ce1 EXPORTING value(p2) TYPE i.
METHODS:trigger."事件触发方法
ENDCLASS.
CLASS c1 IMPLEMENTATION.
METHOD trigger.
RAISE EVENT: e1 EXPORTING p1 = 'A',e2,ce1 EXPORTING p2 = 1.
ENDMETHOD.
ENDCLASS.
静态(以下面的h1方法)或非静(以下面的h2方法)态事件处理方法均可以处理静态或非静态事件,事件的处理方法是否只能处理静态的仍是非静态事件与事件的静态与否没有关系,但事件的触发方法与事件的静态与否有关系(实例方法既可触发静态事件,也可触发非静态事件,但静态方法就只能触发静态事件);可是,事件处理方法虽然能处理的事件与事件的静态与否没有关系,但若是处理的是静态事件,那此处理方法就成为了静态处理器,只能采用静态注册方式对此处理方法进行注册。若是处理的是非静态事件,那此处理方法就是非静态处理器,只能采用非静态注册方式对此处理方法进行注册
处理器的静态与否与处理方法自己是否静态没有关系,只与处理的事件是否静态有关
CLASS c2 DEFINITION."监听器:即事件处理器
PUBLIC SECTION.
"静态方法也能够处理非静态事件,此方法属于非静态处理器,只能采用非静态注册方式
CLASS-METHODS h1 FOR EVENT e1 OF c1 IMPORTING p1 sender.
"非静态方法处理非静态事件,此方法属于非静态处理器,只能采用非静态注册方式
METHODS: h2 FOR EVENT e2 OF c1 IMPORTING sender,
"非静态方法固然更能够处理静态事件,此方法属于静态处理器,只能采用静态注册方式
h3 FOR EVENT ce1 OF c1 IMPORTING p2.
ENDCLASS.
CLASS c2 IMPLEMENTATION.
METHOD h1 .
WRITE: 'c2=>h1'.
ENDMETHOD.
METHOD: h2.
WRITE: 'c2->h2'.
ENDMETHOD.
METHOD: h3.
WRITE: 'c2->h3'.
ENDMETHOD.
ENDCLASS.
DATA: trigger TYPE REF TO c1,
trigger2 TYPE REF TO c1,
handler TYPE REF TO c2.
START-OF-SELECTION.
CREATE OBJECT trigger.
CREATE OBJECT trigger2.
CREATE OBJECT handler.
"因为h1、h2两个处理方法分别是用来处理非静态事件e1、e2的,因此只能采用实例注册方式
SET HANDLER: c2=>h1 handler->h2 FOR trigger,
"h3处理方法是用来处理静态事件ce1的,属于静态处理器,因此只能采用静态注册方式
handler->h3.
trigger->trigger( ).
"虽然trigger( )方法会触发 e1,e2,ce1 三种事件,但h1、h2未向实例trigger2注册,而h3属于静态处理器,与实例无关,即比如向全部实例注册过了同样
trigger2->trigger( ).
3. 内表
3.1.LOOP AT循环内表
LOOP AT itab {INTO wa}|{ASSIGNING <fs> [CASTING]}|{TRANSPORTING NO FILDS}
[[USING KEY key_name|(name)] [FROM idx1] [TO idx2] [WHERE log_exp|(cond_syntax)]].
ENDLOOP.
FROM … TO: 只适用于标准表与排序表 WHERE … : 适用于全部类型的内表
若是没有经过USING KEY选项的key_name,则循环读取的顺序与表的类型相关:
l标准表与排序表:会按照primary table index索引的顺序一条条的循环,且在循环里SY-TABIX为当前正在处理行的索引号
l 哈希表:因为表没有排序,因此按照插入的顺序来循环处理,注,此时SY-TABIX 老是0
能够在循环内表时增长与删除当前行:If you insert or delete lines in the statement block of a LOOP , this will have the following effects:
- If you insert lines behind(后面) the current line, these new lines will be processed in the subsequent loop(新行会在下一次循环时被处理) passes. An endless loop(可能会引发死循环)can result
- If you delete lines behind the current line, the deleted lines will no longer be processed in the subsequent loop passes
- If you insert lines in front(前面) of the current line, the internal loop counter is increased by one with each inserted line. This affects sy-tabix in the subsequent loop pass(这会影响在随后的循环过程SY-TABIX)
- If you delete lines in front of the current line, the internal loop counter is decreased by one with each deleted line. This affects sy-tabix in the subsequent loop pass
3.1.1.循环中删除行
DATA : BEGIN OF gt_table OCCURS 0,
c,
END OF gt_table.
APPEND 'a' TO gt_table.
APPEND 'b' TO gt_table.
APPEND 'c' TO gt_table.
APPEND 'd' TO gt_table.
APPEND 'e' TO gt_table.
APPEND 'f' TO gt_table.
APPEND 'g' TO gt_table.
APPEND 'h' TO gt_table.
LOOP AT gt_table .
IF gt_table-c = 'b' OR gt_table-c = 'c' OR gt_table-c = 'e'.
WRITE : / sy-tabix COLOR = 6 INTENSIFIED ON INVERSE OFF ,
gt_table COLOR = 6 INTENSIFIED ON INVERSE OFF .
ELSE.
WRITE : / sy-tabix, gt_table.
ENDIF.
ENDLOOP.
SKIP 2.
DATA count TYPE i .
LOOP AT gt_table .
count = count + 1.
"当循环到第三次时删除,即循环到 C 时进行删除
IF count = 3.
DELETE gt_table WHERE c = 'b' OR c = 'c' OR c = 'e'.
ENDIF.
"删除以后sy-tabix会从新开始对内表现有的行进行编号
WRITE :/ sy-tabix, gt_table.
ENDLOOP.
SKIP 2.
LOOP AT gt_table .
WRITE : / sy-tabix, gt_table.
ENDLOOP.
3.1.2.SUM
若是在 AT - ENDAT 块中使用 SUM,则系统计算当前行组中全部行的数字字段之和并将其写入工做区域中相应的字段中
3.1.3. AT...ENDAT
<line> |
含义 |
FIRST |
内表的第一行时触发 |
LAST |
内表的最后一行时触发 |
NEW <f> |
相邻数据行中相同<f>字段构成一组,在循环到该组的开头时触发 |
END Of <f> |
相邻数据行中相同<f>字段构成一组,在循环到该组的最末时触发 |
在使用AT...... ENDAT以前,一这要先按照这些语句中的组件名进行排序,且排序的顺序要与在AT...... ENDAT语句中使用顺序一致,排序与声明的顺序决定了先按哪一个分组,接着再按哪一个进行分组,最后再按哪一个进行分组,这与SQL中的Group By 类似
用在AT...... ENDAT语句中的中的组件名不必定要是结构中的关键字段,但这些字段必定要按照出如今AT关键字后面的使用顺序在结构最前面进行声明,且这些组件字段的声明之间不能插入其余组件的声明。如如今须要按照<f1>, <f2>, ....多个字段的顺序来使用在AT...... ENDAT语句中,则首先须要在结构中按照<f1>, <f2>, ....,多字段的顺序在结构最前面都声明,而后按照<f1>, <f2>, ....,多字段来排序的,最后在循环中按以下的顺序块书写程序(请注意书写AT END OF的顺序与AT NEW 是相反的,像下面这样):
LOOP AT <itab>.
AT FIRST. ... ENDAT.
AT NEW <f1>. ...... ENDAT.
AT NEW <f2>. ...... ENDAT.
.......
<single line processing>
.......
AT END OF <f2>.... ENDAT.
AT END OF <f1>. ... ENDAT.
AT LAST. .... ENDAT.
ENDLOOP.
一旦进入到 AT...<f1>...ENDAT 块中时,当前工做区(或表头)中的从<f1>日后,但不包括<f1>(按照在结构中声明的次序)全部字段的字符类型字段会以星号(*)号来填充,而数字字设置为初始值(注:在测试过程当中发现String类型不会使用*来填充,而是设置成empty String,因此只有固定长度类型的非数字基本类型才设置为*)。若是在 AT 块中使用了SUM,则会将全部数字类型字段统计出来将存入当前工做区(或表头);但一旦离开AT....ENDAT块后,又会将当前遍历的行恢复到工做区(或表头)中
DATA: BEGIN OF th_mseg OCCURS 10,
matnr TYPE mard-matnr, werks TYPE mard-werks,
lgort TYPE mard-lgort, shkzg TYPE mseg-shkzg,
menge TYPE mseg-menge, budat TYPE mkpf-budat,
LOOP AT th_mseg.
AT END OF shkzg."会根据shkzg及前面全部字段来进行分组
sum.
WRITE: / th_mseg-matnr, th_mseg-werks,th_mseg-lgort,
th_mseg-shkzg,th_mseg-menge,th_mseg-budat.
ENDAT.
ENDLOOP.
AS-101 2300 0001 S 10.000 ****.**.**
AS-100 2300 0002 S 10.000 ****.**.**
AS-100 2300 0001 S 20.000 ****.**.**
上面因为没有根据matnr + werks + lgort + shkzg 进行排序,因此结果中的第三行其实应该与第一行合并。其实这个统计与SQL里的分组(Group By)统计原理是同样的,Group By 后面须要明确指定分组的字段,如上面程序使用SQL分组写法应该为 Group Bymatnr werks lgort shkzg,但在ABAP里你只须要按照 matnr werks lgort shkzg按照前后顺序在结构定义的最前面进行声明就可表达了Group By那种意义,并且不必定要将matnr werks lgort shkzg这四个字段所有用在AT语句块中AT NEW、AT END OF shkzg 才正确,其实像上面程序同样,只写AT END OF shkzg这一个语句,前面三个字段matnr werks lgort均可以不用在AT语句中出现,由于ABAP默认会按照结构中声明的顺序将shkzg前面的字段也全都用在了分组中了
DATA: BEGIN OF line,
"C2、C3组件名声明的顺序必定要与在AT...... ENDAT块中使用的次序一致,即这里不能将C3声明在C2以前,且不能在C2与C3之间插入其余字段的声明
c2(5) TYPE c,
c3(5) TYPE c,
c4(5) TYPE c,
i1 TYPE i,
i2 TYPE i,
c1(5) TYPE c,
END OF line.
"使用在AT...... ENDAT语句中的字段不必定要是关键字段
DATA: itab LIKE TABLE OF line WITH HEADER LINE WITH NON-UNIQUE KEY i1.
PERFORM append USING 2 'b' 'bb' 'bbb' '2222' 22.PERFORM append USING 3 'c' 'aa' 'aaa' '3333' 33.
PERFORM append USING 4 'd' 'aa' 'bbb' '4444' 44.PERFORM append USING 5 'e' 'bb' 'aaa' '5555' 55.
PERFORM append USING 6 'f' 'bb' 'bbb' '6666' 66.PERFORM append USING 7 'g' 'aa' 'aaa' '7777' 77.
PERFORM append USING 8 'h' 'aa' 'bbb' '8888' 88.
SORT itab ASCENDING BY c2 c3.
LOOP AT itab.
WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2.
ENDLOOP.
SKIP.
LOOP AT itab.
AT FIRST.
WRITE:/ '>>>> AT FIRST'.
ENDAT.
AT NEW c2.
WRITE: / ' >>>> Start of' , itab-c2.
ENDAT.
AT NEW c3.
WRITE: / ' >>>> Start of' , itab-c2, itab-c3.
ENDAT.