ABAP技术总结

 

 

 

 

 

 

 

 

 

 

 

 

 

SAP

——ABAP/4 技术总结 V3.0

 

 

2014-10-14

 

--江正军

 

 

 

 

 

1.      基础... 1

1.1.           基本数据类型... 1

1.1.1.        P类型(压缩型)数据... 1

1.2.           TYPELIKE. 2

1.3.           DESCRIBE. 3

1.4.           字符串表达式... 3

1.5.           Data elementDomain. 4

1.6.           词典预定义类型与ABAP类型映射... 5

1.7.           字符串处理... 7

1.7.1.        countmatch结合... 7

1.7.2.        FIND …SUBMATCHES. 8

1.7.3.        FIND …RESULTS  itab. 8

1.7.4.        正则式类... 9

1.7.4.1.             matchesmatch. 9

1.7.4.2.             contains. 10

1.7.4.3.             find_all10

1.7.4.4.             find_next. 11

1.7.4.5.             get_lengthget_offsetget_submatch. 11

1.7.4.6.             replace_all12

1.8.           CLEARREFRESHFREE. 12

1.9.           ABAP程序中的局部与全局变量... 12

1.10.         FormFunction. 13

1.10.1.     FORM.. 13

1.10.2.     FUNCTION.. 15

1.10.2.1.           Function Group结构... 15

1.10.2.2.           Function参数传值、传址... 18

1.11.         字段符号FIELD-SYMBOLS. 20

1.11.1.     ASSIGN隐式强转... 21

1.11.2.     ASSIGN显示强转... 21

1.11.3.     ASSIGN 动态分配... 21

1.11.4.     UNASSIGNCLEAR. 21

1.12.         数据引用、对象引用... 21

1.12.1.     数据引用Data References. 21

1.12.2.     对象引用Object references. 22

1.12.3.     GET REFERENCE OF获取变量/对象/常量地址... 22

1.13.         动态语句... 22

1.13.1.     内表动态访问... 22

1.13.2.     动态类型... 23

1.13.3.     动态SQL. 23

1.13.4.     动态调用类的方法... 23

1.13.5.     ASSIGN 动态分配... 23

1.13.5.1.           动态访问类的属性成员... 24

1.14.         反射... 24

1.14.1.     TYPE HANDLE. 24

1.14.2.     动态创建数据Data或对象Object. 25

1.14.3.     动态创建基本类型变量、结构、内表... 25

1.14.4.     类对象反射... 26

2.      面向对象... 27

2.1.           类与接口定义... 27

2.1.1.        components. 27

2.2.           类定义、实现... 27

2.3.           接口定义、实现... 27

2.4.           类、接口继承... 28

2.5.           向下强转型 ?=. 28

2.6.           方法... 28

2.6.1.        parameters. 29

2.6.2.        PREFERRED PARAMETER首选参数... 29

2.6.3.        普通调用... 29

2.6.4.        简单调用... 30

2.6.5.        函数方法... 30

2.7.           mesuper. 30

2.8.           事件... 30

2.8.1.        事件定义... 30

2.8.2.        事件触发... 31

2.8.3.        事件处理器Event Handler. 31

2.8.4.        注册事件处理器... 32

2.8.5.        示例... 32

3.      内表... 33

3.1.           LOOP AT循环内表... 33

3.1.1.        SUM.. 34

3.1.2.        AT...ENDAT. 34

3.1.3.        自已实现AT...ENDAT. 37

3.2.           LOOP AT中修改当前内表行... 39

3.2.1.        循环中修改索引表... 39

3.2.2.        循环中修改HASH... 40

3.3.           第二索引... 40

3.3.1.        使用第二索引... 41

3.3.2.        示例... 41

3.4.           适合所有类型的内表操作... 42

3.5.           适合索引内表操作... 43

4.      OPEN SQL. 43

4.1.           SELECT INSERTUPDATEDELETEMODIFY. 43

4.2.           条件操作符... 44

4.3.           RANG条件内表... 44

4.4.           FOR ALL ENTRIES. 45

4.5.           INNER JOINLEFT OUTER JOIN使用限制... 46

4.6.           动态SQL. 46

4.7.           子查询... 47

4.7.1.        =<><<=>>=子查询... 47

4.7.1.1.             ALLANYSOME. 48

4.7.2.        [NOT] IN子查询... 48

4.7.3.        [NOT] EXISTS子查询... 48

4.7.4.        相关子查询... 48

4.8.           统计函数... 48

4.9.           分组过滤... 48

4.10.         游标... 49

4.11.         三种缓存... 49

4.12.         Native SQL. 50

4.12.1.     查询... 50

4.12.2.     存储过程... 50

4.12.3.     游标... 50

4.13.         SAP... 51

5.      SAP/DB LUW.. 51

5.1.           DB LUW.. 51

5.1.1.        显式提交... 52

5.1.2.        隐式提交... 52

5.1.3.        显示回滚... 52

5.1.4.        隐式回滚... 52

5.2.           SAP LUW.. 53

5.2.1.        SAP LUW的绑定方式... 54

5.2.1.1.             Function. 54

5.2.1.2.             subroutine. 55

5.2.2.        开启新的SAP LUW.. 55

5.2.3.        同步或异步更新(提交)... 55

5.2.4.        本地、非本地方式提交... 55

6.      逻辑数据库... 56

6.1.           组成... 56

6.2.           结构... 56

6.3.           选择屏幕(Selections... 57

6.3.1.        PARAMETERS屏幕参数扩充... 58

6.3.2.        SELECTION-SCREEN格式化屏幕... 58

6.3.3.        DYNAMIC SELECTIONS动态选择条件... 58

6.3.3.1.             DYN_SEL. 60

6.3.3.1.1.          RSDS_TYPE-CLAUSES. 60

6.3.3.1.2.          RSDS_TYPE-TRANGE. 61

6.3.4.        FIELD SELECTION动态选择字段... 62

6.3.4.1.             SELECT_FIELDS. 63

6.4.           数据库程序中重要FORM.. 65

6.5.           LDB选择屏幕:静()态选择屏幕、动态选择视图... 66

7.      ALV. 70

7.1.           Layout重要字段... 70

7.2.           FIELDCATALOG重要字段... 70

7.3.           指定双击触发的FunCode. 71

7.4.           相关函数... 71

7.5.           重要参数接口... 71

7.6.           让预置按钮回调I_CALLBACK_USER_COMMAND.. 72

7.7.           颜色... 72

7.8.           可编辑... 72

7.9.           单元格数据修改后立即自动刷新... 73

7.10.         数据有效性验证事件:data_changed. 73

7.11.         金额、数字类型输入问题... 74

7.12.         排序、分类汇总... 74

7.13.         可打印的表头输出... 75

7.14.         布局变式读取、切换、根据布局格式导出数据... 75

7.15.         动态内表... 76

8.      OO ALV. 77

8.1.           相关类... 77

8.2.           控制区域、容器、Grid关系... 77

8.3.           CL_GUI_ALV_GRID重要方法... 77

8.4.           set_table_for_first_dispaly()方法重要参数... 77

8.5.           事件绑定、触发、回调处理... 77

8.6.           CL_GUI_DOCKING_CONTAINER容器... 78

8.7.           覆盖(拦截)预设按钮的功能FunCodeBEFORE_USER_COMMAND.. 78

8.8.           数据改变事件data_changeddata_changed_finished. 79

8.9.           单元格可编辑... 79

9.      问题... 79

9.1.           ALV自带导出文件时字段数据末尾被截断问题... 79

9.2.           Smartform Template无法显示减号后面内容... 80

9.3.           Smartform金额或者数量字段显示不出来... 80

9.4.           更新数据库表时,工作区或内表的结构需参考数据库表来定义... 80

9.5.           DELETE ADJACENT DUPLICATES…去重复... 80

9.6.           Text使用Excel打开乱码问题... 80

9.7.           VBFAEKPO联合查询问题... 81

10.             技巧... 81

10.1.         READ TABLE...WITH KEY可使用OR条件或其他非“=”操作符... 81

10.2.         SELECT SINGLE ... WHERE...无法排序问题... 82

10.3.         当心Where后的条件内表为空时... 82

10.4.         快速查找SO所对应的交货单DNPO.. 82

10.5.         X类型的C类型视图... 82

10.6.         字符串连接:&& 替代 CONCATENATE. 83

10.7.         Variant变式中动态日期... 83

11.             优化... 84

11.1.         数据库... 84

11.2.         程序... 86

12.             屏幕... 88

12.1.         AT SELECTION-SCREENPAIAT USER-COMMAND触发时机... 88

12.2.         SELECTION-SCREEN格式化屏幕、激活预设按钮... 88

12.3.         PARAMETERS. 88

12.4.         SELECT-OPTIONS. 89

12.4.1.     输入ABAP程序默认值时,需要加上“=”. 89

12.4.2.     选择条件内表多条件组合规则... 89

12.4.3.     使用SELECT-OPTIONS替代PARAMETERS. 90

12.5.         各种屏幕元素演示... 91

12.6.         按钮、单选复选框、下拉框的FunCode. 91

12.6.1.     选择屏幕中的按钮... 92

12.6.2.     选择屏幕中的单选/复选按钮:点击时显示、隐藏其他屏幕元素... 92

12.6.3.     选择屏幕中下拉列表:AS LISTBOX. 93

12.7.         屏幕流逻辑... 93

12.7.1.     FIELD.. 93

12.7.2.     MODULE. 94

12.7.3.     ON INPUTON CHAIN-INPUT区别... 94

12.8.         EXIT-COMMAND.. 95

12.8.1.     MODULE <mod> AT EXIT-COMMAND.. 95

12.8.2.     AT SELECTION-SCREEN ON EXIT-COMMAND.. 95

12.9.         OK_CODE. 95

12.9.1.     ok_code使用前需拷贝... 95

12.10.       Search help F4... 95

12.10.1.            VALUE CHECKfixed ValuesValue Table. 95

12.10.2.            检查表Check Table --- Value Table. 96

12.10.3.            SE11检查表与搜索帮助关系... 96

12.10.4.            F4搜索帮助联动的决定因素... 98

12.11.       搜索帮助参数说明... 100

12.12.       F4IF_SHLP_EXIT_EXAMPLE帮助出口... 102

12.12.1.            修改数据源... 102

12.12.2.            删除重复... 103

12.13.       搜索帮助优先级... 103

12.14.       搜索帮助创建函数... 103

12.15.       POV事件里读取屏幕字段中的值函数... 104

12.16.       动态修改屏幕... 104

12.17.       子屏幕... 105

12.18.       屏幕跳转... 106

12.18.1.            CALL SCREEN误用... 106

12.18.2.            CALL SCREEN/SET SCREEN/LEAVE TO SCREEN区别... 107

12.19.       修改标准选择屏幕的GUI Status. 107

12.20.       事件分类... 107

12.20.1.            报表事件... 107

12.20.2.            选择屏幕事件... 107

12.20.3.            逻辑数据库事件... 108

12.20.4.            列表事件... 108

12.20.5.            事件流图... 109

12.21.       事件终止... 110

12.21.1.            RETURN.. 110

12.21.2.            STOP. 110

12.21.3.            EXIT. 110

12.21.4.            CHECK. 110

12.21.5.            LEAVE. 111

12.21.5.1.         REJECT. 111

13.             列表屏幕... 111

13.1.         标准LIST. 112

13.2.         自定义LIST. 112

13.3.         LIST事件... 113

13.4.         Detail Lists 创建... 113

13.5.         标准的 List Status. 113

13.6.         列表屏幕上的数据与程序间的传递... 114

13.6.1.     SY-LISEL. 114

13.6.2.     HIDE. 114

13.6.3.     READ LINE. 114

13.7.         Screen Processing 屏幕处理切换到Lists列表输出... 115

13.8.         LIST 打印输出... 115

14.             Messages. 115

14.1.         00消息ID中的通用消息... 115

14.2.         消息常量... 116

14.3.         静态指定... 116

14.4.         动态指定... 116

14.5.         消息拼接MESSAGE …INTO.. 116

14.6.         修改消息显示性为…DISPLAY LIKE….. 116

14.7.         RAISING <exc>:消息以异常形式抛出... 116

14.8.         CALL FUNCTION…EXCEPTIONS. 117

14.8.1.     error_message = n_error捕获消息... 118

14.9.         各种消息的显示及处理... 118

14.10.       异常处理... 119

14.10.1.            RAISE [EXCEPTION]…触发异常... 119

14.10.1.1.         触发类异常... 119

14.10.1.2.         RESUMABLE选项... 120

14.10.2.            捕获异常... 121

14.10.2.1.         类异常捕获TRY…CATCH.. 121

14.10.2.2.         老式方式捕获runtime errors(运行时异常)121

14.10.3.            向上抛出异常... 121

14.10.4.            类异常... 122

15.             数据格式化、转换... 123

15.1.         数据输入输出转换... 123

15.1.1.     输出时自动转换... 123

15.1.2.     输入时自动转换... 124

15.1.3.     通过转换规则输入输出函数手动转换... 124

15.2.         数量小位数格式化... 125

15.2.1.     案例... 126

15.3.         单位换算:UNIT_CONVERSION_SIMPLE. 128

15.4.         货币格式化... 129

15.4.1.     从表中读取日元并正确的格式化输出... 130

15.4.2.     SAP 货币转换因子... 131

15.4.3.     货币内外格式转换... 133

16.             业务... 134

16.1.         表、业务流程... 134

16.2.         MM.. 138

16.2.1.     常用表... 138

16.2.2.     库存... 139

16.2.3.     物料凭证... 139

16.3.         SD.. 139

16.3.1.     ... 139

16.3.2.     定价过程... 141

16.3.2.1.           条件技术七要素... 141

16.3.2.2.           条件表V/03V/04V/05. 142

16.3.2.3.           存取顺序 V/07. 142

16.3.2.4.           条件类型 V/06. 142

16.3.2.5.           定价过程V/08与确定OVKK. 143

16.3.2.6.           VK11:价格主数据维护... 146

16.3.2.7.           定价计算:KONV. 147

16.3.2.7.1.       条件类型的计算公式... 147

16.3.2.8.           定价过程示例... 148

16.3.2.9.           销售订单中的定价示例... 148

16.3.2.10.         定价通信表KOMKKOMP. 151

16.3.3.     销售相关的凭证类型、类型... 151

16.4.         业务概念... 154

16.4.1.     售达方、送达方、开票方、付款方... 154

16.4.2.     进项税、销项税... 154

16.4.3.     订单日期、凭证日期、过账日期... 155

16.5.         业务知识... 155

16.5.1.     客户联系人相关信息... 155

16.5.2.     销售订单合作伙伴功能... 156

17.             增强... 157

17.1.         第一代:基于源码增强(子过程subroutine... 157

17.2.         第二代:基于函数出口增强(Function... 157

17.2.1.     示例:采购订单屏幕增强... 159

17.2.1.1.           定义全局变量... 161

17.2.1.2.           子屏幕... 161

17.2.1.3.           屏幕与业务表数据间传递... 162

17.2.1.4.           相关函数说明... 163

17.2.2.     如何快速找到增强... 163

17.3.         第三代:基于类的增强(BADI... 165

17.3.1.     新式BADI创建... 166

17.3.1.1.           定义... 166

17.3.1.2.           实现... 168

17.3.1.3.           过滤器... 170

17.3.1.3.1.       调用... 171

17.3.1.4.           多个BADI/ Enhancement实现时究竟调谁... 172

17.3.2.     经典BADI创建... 173

17.3.2.1.           Filter-Depend.过滤器... 174

17.3.2.1.1.       调用... 175

17.3.2.2.           通过经典BADI扩展自定义程序(菜单、屏幕、功能)... 176

17.3.3.     示例:通过BADI实现采购订单屏幕增强... 179

17.4.         第四代:Enhancement-Point. 179

17.4.1.     为自己程序创建显示增强... 180

17.4.2.     隐式与显示增强... 182

18.             数据批量维护... 182

18.1.         BDCSM35SHDB... 182

18.2.         LSMW.. 184

18.3.         业务对象和BAPI184

18.3.1.     SAP业务对象(SWO1... 184

18.3.1.1.           业务对象类型的组成... 185

18.3.1.2.           业务对象(BO)设计... 185

18.3.1.2.1.       创建业务表... 185

18.3.1.2.2.       创建业务对象类型... 186

18.3.1.2.3.       添加(继承)接口... 186

18.3.1.2.4.       添加关键字段Key. 187

18.3.1.2.5.       添加属性... 187

18.3.1.2.6.       通过报表程序来实现业务对象的方法... 189

18.3.1.2.6.1.    报表程序... 189

18.3.1.2.6.2.    重定义接口与方法实现... 190

18.3.1.2.6.3.    测试... 191

18.3.1.2.7.       通过BAPI函数来实现业务对象方法... 192

18.3.1.2.7.1.    创建BAPI参数结构... 192

18.3.1.2.7.2.    创建BAPI函数、BAPI调用返回RETURN结果处理... 193

18.3.1.2.7.3.    BAPI函数绑定到相应的业务方法... 195

18.3.2.     BAPI197

18.3.2.1.           BAPI浏览器... 197

18.3.2.2.           SE37查找:BAPI函数的命名规则... 198

18.3.2.3.           查找某事务码所对应的BAPI198

18.3.2.4.           常用BAPI函数... 199

18.3.2.5.           调用BAPI199

18.3.2.5.1.       BAPI事务处理... 200

18.3.2.5.2.       外部系统(Java)调用BAPI函数... 201

18.3.2.5.2.1.    直连、连接池... 201

18.3.2.5.2.2.    访问结构... 202

18.3.2.5.2.3.    访问表 (Table)203

18.3.2.5.2.4.    Java多线程调用有/无状态RFM.. 204

18.3.2.5.3.       ABAP访问Java服务... 204

18.3.2.5.4.       ABAP创建远程目标... 204

18.3.2.5.5.       连接异常registrationnot allowed. 205

18.3.2.5.6.       带状态访问... 206

18.4.         IDoc. 206

18.4.1.     数据段类型和数据段定义(WE31... 206

18.4.2.     IDoc定义(WE30... 207

18.4.3.     自定义IDoc发送与接收实例... 208

18.4.3.1.           发送端800outbound)配置... 208

1、创建segmentWE31... 208

2、创建IDOC TypeWE30... 209

3、创建Message TypeWE81... 210

4、关联Message TypeIDOC TypeWE82... 210

5、创建接收端RFC DestinationSM59... 210

6、创建到收端的端口(WE21... 211

7、创建发送端Logical System并分配(SALE... 211

8、创建接收端Logical SystemSALE... 212

9、创建接收端合作和伴配置文件Partner profileWE20... 212

10、通过ABAP程序发送IDOC. 213

18.4.3.2.           接收端810Inbound)配置... 216

1、创建发送端RFC DestinationSM59... 216

2、创建发送端的端口(WE21... 217

3、将接收端Logical System分配到Client 810SALE... 217

4、创建入站处理函数... 218

5、注册入站处理函数(BD51... 219

6、将入站函数与IDOC Type/Message Type关联(WE57... 219

7、创建入站处理代码Inbound Process CodeWE42... 219

8、创建发送端合作和伴配置文件Partner profileWE20... 219

9、测试 BD87. 220

19.             数据共享与传递... 222

19.1.         程序调用、会话、SAP/ABAP内存 关系... 222

19.2.         ABAP Memory数据共享... 224

19.2.1.     EXPORT. 224

19.2.2.     IMPORT. 226

19.2.3.     DELETE. 227

19.3.         SAP MEMORY数据共享... 228

19.3.1.     PARAMETERS/SELECT-OPTIONS选项MEMORY ID.. 228

19.3.2.     GET/SET PARAMETER ID.. 228

19.4.         DATABASE. 229

19.4.1.     将文件存入表中... 230

19.4.2.     从表中读取文件... 232

19.5.         JOB间数据传递... 233

20.             拾遗... 233

20.1.         Function调用... 233

20.1.1.     更新FMLUW.. 233

20.1.2.     RFC函数:远程调用... 234

20.1.2.1.           同步... 234

20.1.2.2.           异步... 234

20.1.2.2.1.       事务性RFC调用... 234

20.1.2.3.           DESTINATION 取值... 234

20.2.         函数、类... 235

20.3.         FTP. 235

20.4.         文件读写... 235

20.5.         Email236

20.6.         XML. 236

20.6.1.     生成... 237

20.6.2.     解析... 240

20.7.         OLE. 242

20.7.1.     导出Exel文件多种方式... 243

20.8.         ABAP示例代码... 244

20.9.         长文本... 244

20.9.1.     物料长文本... 244

20.9.2.     生产定单长文本... 245

20.9.3.     采购定单长文本... 246

20.9.4.     销售定单长文本... 246

20.10.       Smart Forms. 246

20.11.       BOM.. 247

20.12.       传输请求SE01SE09SE10. 247

20.13.       Script Form传输:SCC1. 247

20.14.       权限检查... 247

20.15.       允许对表数据维护... 248

20.16.       SE93创建事务码... 248

20.17.       表字段初始值、NULL等问题... 249

20.17.1.            SE11表设置中的Initial Values. 249

20.17.2.            底层数据库表字段默认值... 249

20.17.3.            ABAP初始值、底层数据库表默认值相互转换... 250

20.17.3.1.         向表中插入初始值... 250

20.17.3.2.         读取数据... 251

20.17.4.            SAP系统中的表字段不允许为NULL的原因... 251

20.18.       ABAP中的“空”、INITIAL. 251

20.19.       调试工具... 252

20.19.1.            ST05. 252

20.20.       程序创建Job(报表自已设置后台运行,前后台数据共享)... 253

20.21.       SE78SWM0. 254

20.22.       客户端文本文件或Excel文件上传与下载... 255

20.22.1.            读取客户端TxtExcel文件到内表:TEXT_CONVERT_XLS_TO_SAP. 255

20.22.2.            将数据内表导出为EXCEL文件:SAP_CONVERT_TO_XLS_FORMAT. 256

20.23.       Unicode字符串互转... 256

20.24.       字符编码与解码... 256

20.25.       ABAP中的特殊字符列表... 257

20.26.       下载文件... 257

20.26.1.            BIN二进制下载... 257

20.26.2.            以字符模式下载... 258

20.27.       将文件上传到数据库表中,并可邮件发送... 259

20.28.       AppendInclude系统表结构增强... 261

20.29.       结构复用(INCLUDE... 262

20.30.       常用事务码... 263

21.             常用Function. 265

21.1.         日期函数... 265

21.1.1.     日期、时间验证... 265

21.1.2.     内部转换外部格式... 265

21.1.3.     外部转内部格式... 266

21.1.4.     获取Client格式... 267

21.1.5.     日期加减... 267

21.1.6.     转成工厂日期... 267

21.1.7.     日期属性... 269

21.1.8.     节假日... 270

21.1.9.     年月选择框... 271

21.1.10.            财政年... 271

21.1.11.            星期翻译对照表... 271

21.1.12.            日期所在周末、天/周、周/... 272

 

 

1.   基础

1.1.  基本数据类型

CNDTIFPXstringXstring

P默认为8字节,最大允许16字节。最大整数位:16*2 = 32 - 1 = 31 -14(允许最大小数位数) = 17位整数位

类型

最大长度(字符数)

默认长度

说明

C

1~262143个字符

1 字符

 

N

1~262143个字符

1 字符

09之间字符组成的数字字符串

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类型看用整型。其效率低于IF类型。

"16 * 2 = 32表示了整个字面意义上允许的最大字面个数,而14表示的是字面上小数点后面允许的最大小数位,而不是指14个字节,只有这里定义时的16才表示16个字节

DATAp(16TYPE DECIMALS 14 VALUE '12345678901234567.89012345678901'.

"正负符号与小数点固定要占用半个字节,一个字面上位置,并包括在这16个字节里面。
"16 * 2 = 32位包括了小数点与在正负号在内
"在定义时字面上允许最长可以达到32位,除去小数点与符号需占半个字节以后
"有效数字位可允许31位,这31位中包括了整数位与小数位,再除去定义时小
"数位为14位外,整数位最多还可达到17位,所以下面最多只能是179
DATAp1(16TYPE DECIMALS 14 VALUE '-99999999999999999'.

 

"P类型是以字符串来表示一个数的,与字符串不一样的是,P类型中的每个数字位只会占用4Bit位,所以两个数字位才会占用一个字节。另外,如果定义时没有指定小数位,表示是整型,但小数点固定要占用半个字节,所以不带小数位与符号的最大与最小整数如下(最多允许319,而不是32个)
DATA: p1(16TYPE p  VALUE '+9999999999999999999999999999999'.
DATA: p2(16TYPE p  VALUE '-9999999999999999999999999999999'.

 

其实P类型是以字符串形式来表示一个小数,这样才可以作到精确,就像Java中要表示一个精确的小数要使用BigDecimal一样,否则会丢失精度。

DATAp(9TYPE DECIMALS VALUE '-123456789012345.12'.
WRITE/ p."123456789012345.12-

 

DATAf1 TYPE VALUE '2.0',
      f2 
TYPE VALUE '1.1',
      f3 
TYPE f.
f3  
=  f1 f2."不能精确计算
"2.0000000000000000E+00 1.1000000000000001E+00 8.9999999999999991E-01
WRITE/ f1   f2 f3.

DATAp1 TYPE DECIMALS VALUE '2.0',
      p2 
TYPE DECIMALS VALUE '1.1',
      p3 
TYPE 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.TYPELIKE

透明表(还有其它数据词典中的类型,如结构)即可看作是一种类型,也可看作是对象,所以即可使用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,就会出现这两个关键字(TypeLike)都可用的奇怪现像下面是定义一个变量时与词典中的结构同名的后果(导致)

DATA : BEGIN OF address2,
  street(
20TYPE c,
  city(
20TYPE 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|CHARACTERMODE
  [
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 |{ }|."算术计算表达式
str |{ |aa| && 'bb' }|."字符串表达式

str |{ str }|."变量名

str |{ strlenstr }|."内置函数

1.5.  Data elementDomain

数据元素是构成结构、表的基本组件域又定义了数据元素的技术属性Data element主要附带Search HelpParameter 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 elementDomain时可以引用

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类型也是这样);空格去掉后会左对齐[kənˈdens] 

CONCATENATE {dobj1 dobj2 ...}|{LINES OF itab}[kənˈkatɪneɪt]
            INTO result
            [
SEPARATED BY sep]
            [
RESPECTING BLANKS].

CDNT类型的前导空格会保留,尾部空格都会被去掉,但对String类型所有空格都会保留;对于c, d, n, t类型的字符串有一个RESPECTING BLANKS选项可使用,表示尾部空格也会保留。注:使用 `` String类型进行赋值时才会保留尾部空格  字符串连接可以使用 && 来操作,具体请参考这里

strlen(arg)Xstrlen(arg)String类型的尾部空格会计入字符个数中,但C类型的变量尾部空格不会计算入

substringval TEXT [off off] [len len] )

count( val TEXT {sub substring}|{regex regex} )匹配指定字符串substring或正则式regex出现的子串次数,返回的类型为i整型类型

containsval TEXT REGEX REGEX)是否包含。返回布尔值,注:只能用在ifWhile等条件表达式中

matchesval TEXT REGEX REGEX)regex表达式要与text完全匹配,这与contains是不一样的。返回布尔值,也只能用在ifWhile等条件表达式中

matchval TEXT REGEX REGEX occ occ)返回的为匹配到的字符串。注:每次只匹配一个。occ:表示需匹配到第几次出现的子串。如果为正,则从头往后开始计算,如果为负,则从尾部向前计算

findval 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 ...比分组的数量少,则多余的分组将被忽略;且只存储第一次或最后一次匹配到的结果

replaceval TEXT  REGEX REGEX  WITH NEW)使用new替换指定的子符串,返回String类型

REPLACE ALL OCCURRENCES OF REGEX regex IN  dobj WITH new

1.7.1.   countmatch结合

DATAtext TYPE string VALUE `Cathy's cat with the hat sat on Matt's mat.`,
      regx 
TYPE string VALUE `\<.at\>`."\< 单词开头\> 单词结尾
DATAcounts TYPE i,
index TYPE i,
      substr 
TYPE string.
WRITE text.
NEW-LINE.
counts 
countval text regex regx )."返回匹配次数
DO counts TIMES.
index findval   text regex regx occ sy-index )."返回匹配到的的起始位置索引
  substr 
matchval  text regex regx occ sy-index )."返回匹配到的串
index index 1.
WRITE AT index substr.
ENDDO.

1.7.2.FIND …SUBMATCHES 

DATAmoff 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`"会匹配二次,但只会返回第二次匹配到的结果,第一次匹配到的子串不会存储到s1s2s3中去
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

DATAresult 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
.
DATAresult_tab TYPE match_result_tab WITH HEADER LINE.
DATAsubresult_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-offsetresult_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_regexJava中的 java.util.regex.Pattern的类对应

cl_abap_matcherJava中的 java.util.regex.Matcher的类对应

1.7.4.1.matchesmatch

是否完全匹配正则式中不必使用 ^ $);matches为静态方法match为实例方法作用都是一样

DATAmatcher TYPE REF TO cl_abap_matcher,
      match 
TYPE match_result,
      match_line 
TYPE submatch_result.
"^$可以省略因为matches方法本身就是完全匹配整个Regex
IF cl_abap_matcher=>matchespattern '^(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-offsetmatch_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDIF.

DATAmatcher TYPE REF TO cl_abap_matcher,
      match 
TYPE match_result,
      match_line 
TYPE submatch_result.
"^$可以省略,因为matche方法本身就是完全匹配整个Regex
matcher 
cl_abap_matcher=>createpattern '^(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-offsetmatch_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDIF.

1.7.4.2.contains

是否包含也可在正则式中使用 ^ $ 用于完全匹配检查或者使用 ^ 检查是否匹配开头或者使用 $ 匹配结尾

DATAmatcher TYPE REF TO cl_abap_matcher,
      match 
TYPE match_result,
match_line 
TYPE submatch_result.
IF cl_abap_matcher=>containspattern '(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-offsetmatch_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDIF.

1.7.4.3.find_all

一次性找出所有匹配的子串,包括子分组(括号括起的部分)

DATAmatcher TYPE REF TO cl_abap_matcher,
       match_line 
TYPE submatch_result,
      itab 
TYPE match_result_tab WITH HEADER LINE.
matcher 
cl_abap_matcher=>createpattern '<[^<>]*(ml)>' text '<html>hello</html>' )."创建 matcher 实例
":子分组存储在itab-submatches字段里
itab[] matcher->find_all( ).
LOOP AT itab .
WRITE/ matcher->textitab-offsetitab-length,matcher->text+itab-offset(itab-length).
  LOOP AT  itab-submatches INTO match_line"提取子分组(括号括起来的部分)
WRITE/20 match_line-offsetmatch_line-length,matcher->text+match_line-offset(match_line-length).
ENDLOOP.
ENDLOOP.

1.7.4.4.find_next

逐个找出匹配的子串,包括子分组(括号括起的部分)

DATAmatcher 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=>createpattern '<[^<>]*(ml)>' text '<html>hello</html>' ).
WHILE matcher->find_next( ) = 'X'.
  match 
matcher->get_match( )."获取最近一次匹配的结果
WRITE/ matcher->textmatch-offsetmatch-length,matcher->text+match-offset(match-length).  
  
LOOP AT  match-submatches INTO match_line. "提取子分组(括号括起来的部分)
 
WRITE/20 match_line-offsetmatch_line-length,matcher->text+match_line-offset(match_line-length).
  ENDLOOP.
ENDWHILE.

1.7.4.5.get_lengthget_offsetget_submatch

DATAmatcher TYPE REF TO cl_abap_matcher,
      length 
TYPE i,offset TYPE i,
      submatch 
TYPE string.
matcher 
cl_abap_matcher=>createpattern '(<[^<>]*(ml)>)' text '<html>hello</html>' ).
WHILE matcher->find_next( ) = 'X'. "循环2
"0时,表示取整个Regex匹配到的子串,这与Java一样,但如果整个Regex使用括号括起来后,
"则分组索引为1,这又与Java不一样(Java不管是否使用括号将整个Regex括起来,分组索引号都为0
  "
上面Regex中共有两个子分组,再加上整个Regex为隐含分组,所以一共为3
DO TIMES.
"在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串长度
    length 
matcher->get_lengthsy-index ).
"在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串起始位置
    offset 
matcher->get_offsetsy-index ).
"在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串
    submatch 
matcher->get_submatchsy-index ).
WRITE:/ length offset,matcher->text+offset(length),submatch.
ENDDO.
SKIP.
ENDWHILE.

1.7.4.6.replace_all

DATAmatcher TYPE REF TO cl_abap_matcher,
      count
 TYPE i,
      repstr 
TYPE string.
matcher 
cl_abap_matcher=>createpattern '<[^<>]*>' text '<html>hello</html>' ).
count
 matcher->replace_all``)."返回替换的次数
repstr 
matcher->text. "获取被替换后的新串
WRITEcount repstr.

1.8.CLEARREFRESHFREE

内表:如果使用有表头行的内表,CLEAR 仅清除表格工作区域。要重置整个内表而不清除表格工作区域,使用REFRESH语句或 CLEAR 语句CLEAR <itab>[].REFRESH加不加中括号都是只清内表,另外REFRESH是专为清内表的,不能清基本类型变量,但CLEAR可以

以上都不会释放掉内表所占用的空间,如果想初始化内表的同时还要释放所占用的空间,请使用:FREE <itab>.

1.9.ABAP程序中的局部与全局变量

报表程序中选择屏幕事件块(AT SELECTION-SCREEN)与逻辑数据库事件块、以及methods(类中的方法)、subroutinesFORM子过程)、function modulesFunction函数)中声明的变量为局部的,即在这些块里声明的变量不能在其他块里使用,但这些局部变量可以覆盖同名的全局变量;除这些处理块外,其他块里声明的变量都属于全局的(如报表事件块列表事件块、对话Module),效果与在程序最开头定义的变量效果是一样的,所以可以在其他处理块直接使用(但要注意的是,需遵守先定义后使用的原则,这种先后关系是从语句书写顺序来说的,与事件块的本身运行顺序没有关系);另外,局部变量声明时,不管在处理块的任何地方,其效果都是相当于处理块里的全局变量,而不像其他语言如Java那样:局部变量的作用域可以存在于任何花括号{}之间(这就意味着局部变量在处理过程范围内是全局的),如下面的i,在ABAP语言中还是会累加输出,而不会永远是1(在Java语言中会是1):

FORM aa.
DO 10 TIMES.
DATAi TYPE i VALUE 0.
1.
WRITEi.
ENDDO.
ENDFORM.

1.10.FormFunction

FormFunction中的TABLES参数,TYPELIKE后面只能接标准内表类型或标准内表对象,如果要使用排序内表或者哈希内表,则只能使用USINGForm)与CHANGING方式来代替。当把一个带表头的实参通过TABLES参数传递时,表头也会传递过去,如果实参不带表头或者只传递了表体(使用了[]时),系统会自动为内表参数变量创建一个局部空的表头

不管是以TABLES还是以USINGForm非值CHANGE非值方式传递时,都是以引用方式(即别名,不是指地址,注意与Java中的传引用区别:Java实为传值,但传递的值为地址的值,而ABAP中传递的是否为地址,则要看实参是否是通过Type ref to定义的)传递;但如果USING值传递,则对形参数的修改不会改变实参,因为此时不是引用传递;但如果CHANGE值传递,对形参数的修改还是会改变实参,只是修改的时机在Form执行或Function执行完后,才去修改

Form中通过引用传递时,USINGCHANGING完全一样;但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 形式参数

DATAd(10VALUE'11'.
FIELD-SYMBOLS<fs> LIKE d.
ASSIGN TO <fs>.
PERFORM aa USING <fs> d d.
FORM aa USING fs like <fs>  a like b like a.
WRITE:fs,/ a / b.
ENDFORM.

如果没有给形式参数指定类,则为ANY类型

如果TABLESUSINGCHANGING一起使用时,则一定要按照TABLESUSINGCHANGING顺序声明

值传递中的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.
"由于USINGC++的引用参数,所以这里修改的直接是实参所存储的地址内容,这里的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>U01L<fgrp>U02...这些Include文件实际上包含了所对应的Function Module代码(即双击它们进去就是对应的Function,而显示的不是真正Include文件所对应的代码)

lL<fgrp>U01L<fgrp>U02中的0102编号对应L<fgrp>UXX中的“XX”,代表其创建先后的序号,例如L<fgrp>U01L<fgrp>U02是头两个被创建的函数,在函数组中创建出的函数代码就放在相应的L<fgrp>UXX(这里的XX代表某个数字,而不是字面上的XXInclude头文件中

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 ModuleInclude文件等)加载到主调程序所在的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>.
"同上面,REFERENCEIMPORTING类型的参数不能被修改:这里即不能修改实参的指向"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隐式强转

TYPESBEGIN OF t_date,
  year(
4TYPE  n,
  month(
2TYPE n,
  day(
2TYPE n,
END OF t_date.

FIELD-SYMBOLS <fs> TYPE t_date."<fs>定义成了具体限定类型
ASSIGN sy-datum TO <fs> CASTING. "后面没有指定具体类型,所以使用定义时的类型进行隐式转换

1.11.2.ASSIGN显示强转

DATA txt(8TYPE c VALUE '19980606'.
FIELD-SYMBOLS <fs>.
ASSIGN txt TO <fs> CASTING TYPE d."由于定义时未指定具体的类型,所以这里需要显示强转

1.11.3.ASSIGN 动态分配

请参考动态语句à ASSIGN 动态分配

1.11.4.UNASSIGNCLEAR

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

DATAdref TYPE REF TO i ."dref即为数据引用,即数据指针,指向某个变量或常量,存储变量地址
CREATE DATA dref.
dref
->2147483647."可直接解引用使用不需要先通过分配给字段符号后再使用

DATABEGIN OF strct,
c,
END OF strct.
DATAdref LIKE REF TO strct .
CREATE DATA dref .
dref
->*-'A'.

 

TYPEStpy TYPE c.
DATAc1 TYPE REF TO tpy.
DATAc2 LIKE REF TO c1."二级指针
GET REFERENCE OF 'a' INTO c1.
GET REFERENCE OF c1 INTO c2.
WRITEc2->*->*."a

1.12.2.对象引用Object references

CLASS cl DEFINITION.
PUBLIC SECTION.
DATAi VALUE 1.
ENDCLASS.
CLASS cl IMPLEMENTATION.
ENDCLASS.

 

DATAobj TYPE REF TO cl.
CREATE OBJECT obj. "创建对象

DATAoref LIKE REF TO obj. "oref即为对象引用,即对象指针,指向某个对象,存储对象地址
GET REFERENCE OF obj INTO oref. "获取对象地址
WRITEoref->*->i."1

1.12.3.GET REFERENCE OF获取变量/对象/常量地址

DATAe_i3 TYPE REF TO i .
GET REFERENCE OF 33 INTO e_i3.
WRITEe_i3->*."33
"但不能修改常量的值
"e_i3->* = 44.

DATAi TYPE i VALUE 33,
      dref 
LIKE REF TO i."存储普通变量的地址
GET REFERENCE OF i INTO dref.
dref
->44.
WRITEi"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

Select请参照后面的动态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(20TYPE c VALUE 'Output String',
     name
(20TYPE c VALUE 'STR'.

"静态分配:编译时就知道要分配的对象名
ASSIGN name TO <fs>."结果<fs>name变量等同

"通过变量名动态访问变量
ASSIGN (nameTO <fs>."结果是是<fs>的值为str变量值

 

DATABEGIN OF line,
  col1 
TYPE i VALUE '11',
  col2 
TYPE i VALUE '22',
  col3 
TYPE i VALUE '33',
END OF line.
DATA comp(5VALUE '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_TYPEDESCR

  |--CL_ABAP_DATADESCR

  |   |--CL_ABAP_ELEMDESCR

  |   |--CL_ABAP_REFDESCR

  |   |--CL_ABAP_COMPLEXDESCR

  |       |--CL_ABAP_STRUCTDESCR

  |       |--CL_ABAP_TABLEDESCR

  |--CL_ABAP_OBJECTDESCR

     |--CL_ABAP_CLASSDESCR

     |--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(5TYPE c.
datatype ?= cl_abap_typedescr=>describe_by_datafield ).

 

DATA: elemtype TYPE REF TO cl_abap_elemdescr.
elemtype = cl_abap_elemdescr=>get_i( ).
elemtype = cl_abap_elemdescr=>
get_c20 ).

 

DATAoref1 TYPE REF TO object.
DATAdescr_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.

 

DATAdref TYPE REF TO data,
c10type 
TYPE REF TO cl_abap_elemdescr.
c10type 
cl_abap_elemdescr=>get_c10 ).
CREATE DATA dref TYPE HANDLE c10type.

DATAx20type TYPE REF TO cl_abap_elemdescr.
x20type 
cl_abap_elemdescr=>get_x20 ).
FIELD-SYMBOLS<fs> TYPE any.
ASSIGN dref->TO <fs> CASTING TYPE HANDLE x20type.

1.14.2.动态创建数据Data或对象Object

  TYPESty_i TYPE i.
DATAdref TYPE REF TO ty_i .
 CREATE DATA dref TYPE ('I')."根据基本类型名动态创建数据
  dref->1.
WRITE/ dref->*." 1

  CREATE OBJECT oref TYPE ('C1')."根据类名动态创建实例对象

1.14.3.动态创建基本类型变量、结构、内表

DATAdref_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=>createcomp_tab[] ).
CREATE DATA dref_str TYPE HANDLE struct_type."使用结构类型对象来创建结构对象

**=========动态创建内表
基于结构类型对象创建内表类型对象
itab_type cl_abap_tabledescr=>createstruct_type ).
CREATE DATA dref_tab TYPE HANDLE itab_type."使用内表类型对象来创建内表类型
ASSIGN dref_tab->TO <fs_itab>."将字段符号指向新创建出来的内表对象

"**========给现有的内表动态的加一列
table_type ?= cl_abap_tabledescr
=>describe_by_dataitab ).
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_c120 ).
INSERT comp_tab INTO comp_tab INDEX 2.
struct_type 
cl_abap_structdescr=>createcomp_tab[] ).
itab_type 
cl_abap_tabledescr=>createstruct_type ).
CREATE DATA dref_tab TYPE HANDLE itab_type.

1.14.4.类对象反射

CLASS c1 DEFINITION.
PUBLIC SECTION.
DATAc VALUE 'C'.
METHODStest.
ENDCLASS.

CLASS c1 IMPLEMENTATION.
METHOD:test.
WRITE:'test'.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
TYPESty_c.
DATAoref TYPE REF TO object .
DATAoref_classdescr TYPE REF TO cl_abap_classdescr .
CREATE OBJECT oref TYPE ('C1')."根据类名动态创建实例对象
"相当于Java中的Class类对象
 oref_classdescr ?= cl_abap_classdescr
=>describe_by_object_reforef ).

DATAt_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 
/ 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>.[inˈherit]

 

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.

  s ?= p."向下强转型

2.6.  方法

METHODS/CLASS-METHODS meth [ABSTRACT|FINAL
    [
IMPORTING parameters [PREFERRED PARAMETER p]] 
    [
EXPORTING parameters
    [
CHANGING parameters
    [{
RAISING|EXCEPTIONS} exc1 exc2 ...].

应该还有一个Returning选项,RETURNING不能与EXPORTINGCHANGING同时使用

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 }...

²dataobject表示是通用数据类型dataobject

²complete_type为完全限定类型

²OPTIONALDEFAULT两个选项不能同时使用且对于EXPORTING类型的输入参数不能使用

²如果参数名p1前没有使用VALUEREFERENCE默认为还是REFERENCE即引用传递

²方法中的输入输出参数是否能修改请参考FormFunction参数的传值传址

2.6.2.   PREFERRED PARAMETER首选参数

设置多个IMPORTING类型参数中的某一个参数为首选参数。

首选参数的意义在于:当所有IMPORTING类型都为可选optional时,我们可以通过PREFERRED PARAMETER选项来指定某一个可选输入参数为首选参数,则在以下简单方式调用时:[CALL METHOD] meth). 实参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不能与EXPORTINGCHANGING同时使用

[EXCEPTIONS [exc1 = n1 exc2 = n2 ...]

[OTHERS = n_others] ] [)].

如果省略CALL METHOD,则一定要加上括号形式如果通过CALL METHOD来调用,则括号可加可不加

RECEIVING:用来接收METHODS /CLASS-METHODS RETURNING选项返回的值

如果EXPORTINGIMPORTINGCHANGINGRECEIVINGEXCEPTIONSOTHERS同时出现时,应该按此顺序来编写

使用此种方式调用(使用 EXPORTINGIMPORTING等这些选项)时,如果原方法声明时带了返回值RETURNING,只能使用RECEIVING来接受,而不能使用等号来接收返回值,下面用法是错误的:

num2 o1->m1EXPORTING p1 num1 ).

2.6.4.   简单调用

此方式下输入的参数都只能是IMPORTING类型的参数,如果要传CHANGINGEXPORTINGRAISINGEXCEPTIONS类型的参数时,只能使用上面通用调用方式

²meth( )

此种方式仅适用于没有输入参数IMPORTING输入\输出参数CHANGING或者有但都是可选的、或者不是可选时但有默认值也可

²meth( a )

此种方式仅适用于只有一个必选输入参数IMPORTING)(如果还有其他输入参数,则其他都为可选,或者不是可选时但有默认值也可),或者是有多个可选输入参数IMPORTING)(此时没有必选输入参数情况下)的情况下但方法声明时通过使用PREFERRED PARAMETER选项指定了其中某个可选参数为首选参数(首选参数即在使用meth( a )方式传递一个参数进行调用时,通过实参a传递给设置为首选的参数

²meth( p1 = a1 p2 = a2 ... )

此种方式适用于有多个必选的输入参数IMPORTING)方法的调用(其它如CHANGINGEXPORTING没有,或者有但可选),如果输入参数(IMPORTING)为可选,则也可以不必传

2.6.5.函数方法

Return唯一返回值

METHODS meth
    [IMPORTING
parameters [PREFERRED PARAMETER p]]
    RETURNINGVALUE(r) typing
    [{RAISING|EXCEPTIONS} exc1 exc2 ...].

 

RETURNING 用来替换EXPORTINGCHANGING不能同时使用。定义了一个形式参数 r 来接收返回值并且只能是值传递

具有唯一返回值的函数方法可以直接用在以下语句中逻辑表达式IFELSEIFWHILECHECKWAITCASELOOP、算术表达式、赋值语句

函数方法可以采用上面简单调用方式来调用meth( )meth( a )meth( p1 = a1 P2 = a2 ... )

 

  ref->mRECEIVING  r ).
CALL METHOD ref->mRECEIVING r ).
CALL METHOD ref->RECEIVING r i.

2.7.mesuper

等效于Java中的 thissuper

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) ...].

²dataobject:表示是通用数据类型dataobject

²complete_type:为完全限定类型

²  OPTIONALDEFAULT两个选项不能同时使用

²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(p1TYPE string value(p2TYPE 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.
EVENTSe1 EXPORTING value(p1TYPE c,e2.
CLASS-EVENTS ce1 EXPORTING value(p2TYPE i.
METHODS:trigger."事件触发方法
ENDCLASS.

CLASS  c1 IMPLEMENTATION.
METHOD trigger.
RAISE EVENTe1 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.
"非静态方法处理非静态事件此方法属于非静态处理器只能采用非静态注册方式
METHODSh2 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.
METHODh2.
WRITE'c2->h2'.
ENDMETHOD.
METHODh3.
WRITE'c2->h3'.
ENDMETHOD.
ENDCLASS.

DATAtrigger 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.
  "由于h1h2两个处理方法分别是用来处理非静态事件e1e2的,所以只能采用实例注册方式
SET HANDLERc2=>h1 handler->h2 FOR trigger,
"h3处理方法是用来处理静态事件ce1的,属于静态处理器,所以只能采用静态注册方式
                 handler
->h3.
  trigger
->trigger( ).
  "虽然trigger( )方法会触发 e1,e2,ce1 三种事件,但h1h2未向实例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块后,又会将当前遍历的行恢复到工作区(或表头)中

  DATABEGIN 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 NEWAT END OF shkzg 才正确,其实像上面程序一样,只写AT END OF shkzg这一个语句,前面三个字段matnr werks lgort都可以不用在AT语句中出现,因为ABAP默认会按照结构中声明的顺序将shkzg前面的字段也全都用在了分组中了

 

DATABEGIN OF line,
"C2C3组件名声明的顺序一定要与在AT...... ENDAT块中使用的次序一致,即这里不能将C3声明在C2之前,且不能在C2C3之间插入其他字段的声明
      c2
(5TYPE c,
      c3
(5TYPE c,
      c4
(5TYPE c,
      i1 
TYPE i,
      i2 
TYPE i,
      c1
(5TYPE c,
END OF line.

"使用在AT...... ENDAT语句中的字段不一定要是关键字段
DATAitab LIKE TABLE OF line WITH HEADER LINE WITH NON-UNIQUE KEY i1.
PERFORM append USING 'b' 'bb' 'bbb' '2222' 22.PERFORM append USING 'c' 'aa' 'aaa' '3333' 33.
PERFORM append USING 'd' 'aa' 'bbb' '4444' 44.PERFORM append USING 'e' 'bb' 'aaa' '5555' 55.
PERFORM append USING 'f' 'bb' 'bbb' '6666' 66.PERFORM append USING 'g' 'aa' 'aaa' '7777' 77.
PERFORM append USING '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-c2itab-c3.
ENDAT.
  "只要一出 AT 块,则表头的数据又会恢复成当前被遍历行的内容
WRITE/ itab-c2,itab-c3,itab