REPORT ztest_class_ref_data. | |
CLASS lcl_abstract_data DEFINITION ABSTRACT.ENDCLASS. | |
CLASS lcl_abstract_processor DEFINITION ABSTRACT. | |
PUBLIC SECTION. | |
METHODS constructor IMPORTING io_data TYPE REF TO lcl_abstract_data. | |
PROTECTED SECTION. | |
DATA mo_data TYPE REF TO lcl_abstract_data. | |
ENDCLASS. | |
CLASS lcl_abstract_processor IMPLEMENTATION. | |
METHOD constructor. | |
mo_data = io_data. | |
ENDMETHOD. | |
ENDCLASS. | |
CLASS lcl_data DEFINITION INHERITING FROM lcl_abstract_data. | |
PUBLIC SECTION. | |
TYPES tt_data TYPE STANDARD TABLE OF i WITH EMPTY KEY. | |
DATA mt_data TYPE tt_data. | |
CLASS-METHODS cast | |
IMPORTING io_data TYPE REF TO lcl_abstract_data | |
RETURNING VALUE(ro_data) TYPE REF TO lcl_data. | |
METHODS print . | |
ENDCLASS. | |
CLASS lcl_data IMPLEMENTATION. | |
METHOD cast. | |
ro_data ?= io_data. | |
ENDMETHOD. | |
METHOD print. | |
LOOP AT mt_data INTO DATA(lv_value). | |
WRITE lv_value. NEW-LINE. | |
ENDLOOP. | |
ULINE. | |
ENDMETHOD. | |
ENDCLASS. | |
CLASS lcl_processor DEFINITION INHERITING FROM lcl_abstract_processor. | |
PUBLIC SECTION. | |
METHODS process . | |
METHODS process2. | |
METHODS process3. | |
ENDCLASS. | |
CLASS lcl_processor IMPLEMENTATION. | |
METHOD process. | |
DATA(lo_data) = lcl_data=>cast( mo_data ). | |
LOOP AT lo_data->mt_data ASSIGNING FIELD-SYMBOL(<v_value>). | |
ADD 1 TO <v_value>. | |
ENDLOOP. | |
ENDMETHOD. | |
METHOD process2. | |
LOOP AT CAST lcl_data( mo_data )->mt_data ASSIGNING FIELD-SYMBOL(<v_value>). | |
ADD 1 TO <v_value>. | |
ENDLOOP. | |
ENDMETHOD. | |
METHOD process3. | |
LOOP AT lcl_data=>cast( mo_data )->mt_data ASSIGNING FIELD-SYMBOL(<v_value>). | |
ADD 1 TO <v_value>. | |
ENDLOOP. | |
ENDMETHOD. | |
ENDCLASS. | |
START-OF-SELECTION. | |
DATA(lo_data) = NEW lcl_data( ). | |
lo_data->mt_data = VALUE #( ( 1 ) ( 2 ) ). | |
DATA(lo_processor) = NEW lcl_processor( lo_data ). | |
lo_data->print( ). | |
lo_processor->process( ). | |
lo_data->print( ). | |
lo_processor->process2( ). | |
lo_data->print( ). | |
lo_processor->process3( ). | |
lo_data->print( ). |
Код притянут за уши, но смысл его примерно такой. Есть некий базовый класс данных (LCL_ABSTRACT_DATA). От него наследуется конкретный класс с конкретными данными (LCL_DATA).Данные тут таблица целых чисел. Для простоты она сделана публичной. Также есть метод PRINT, который выводит содержимое таблицы на экран.
Также есть некий абстрактный обработчик данных (LCL_ABSTRACT_PROCESSOR), который при создании на вход получает ссылку на инстанцию класса с данными и сохраняет ее у себя в атрибуте (MO_DATA). От него наследуется обработчик для конкретных данных (LCL_PROCESSOR), который работает именно с LCL_DATA. Для этого внутри своих методов он приводит ссылку на LCL_ABSTRACT_DATA к ссылке на LCL_DATA и работает именно с ней. У него есть 3 разных метода (PROCESS,PROCESS2,PROCESS3), где приведение происходит разными способами. И это, как говорится, влияет! В остальном методы делают одно и тоже - прибавляют 1 к каждому числу в таблице
В начале программы создается класс с данными и заполняется двумя числами 1 и 2.
Далее создается класс процессора, куда передается ссылка на созданный класс с данными, после чего выводится начальное содежимое таблицы класса с данными.
Далее последовательно вызываются методы PROCESS,PROCESS2,PROCESS3 у процессора и после каждого вызова происходит вывод текущего состояния таблицы с данными на экран.
Рассмотрим первый метод:
METHOD process.
DATA(lo_data) = lcl_data=>cast( mo_data ).
LOOP AT lo_data->mt_data ASSIGNING FIELD-SYMBOL(<v_value>).
ADD 1 TO <v_value>.
ENDLOOP.
ENDMETHOD.
Здесь вначале вызывается метод lcl_data=>cast для приведения ссылки на LCL_ABSTRACT_DATA к ссылке на LCL_DATA. Полученная ссылка запоминается в переменной LO_DATA. После чего идет цикл по LO_DATA->MT_DATA и к каждом числу в таблице добавляется 1. В итоге значения в таблице должны стать 2 и 3: (1,2) + 1 -> (2,3). Забегая вперед - так и есть. Тут все работает.
Рассмотрим второй метод:
METHOD process2.
LOOP AT CAST lcl_data( mo_data )->mt_data ASSIGNING FIELD-SYMBOL(<v_value>).
ADD 1 TO <v_value>.
ENDLOOP.
ENDMETHOD.
Здесь вначале для приведения ссылки используется конструкция CAST lcl_data( mo_data ) непосредственно в теле цикла. В логике все то же самое - к каждом числу в таблице добавляется 1. В итоге значения в таблице должны стать 3 и 4: (2,3) + 1 -> (3,4). Забегая вперед - так и есть. Тут тоже все работает.
И, наконец, третий метод:
METHOD process3.
LOOP AT lcl_data=>cast( mo_data )->mt_data ASSIGNING FIELD-SYMBOL(<v_value>).
ADD 1 TO <v_value>.
ENDLOOP.
ENDMETHOD.
Здесь вначале для приведения ссылки используется конструкция lcl_data=>cast( mo_data ) как и в первом методе, но ссылка предварительно не запоминается, а результат вызова метода CAST используется непосредственно в теле цикла. В логике все то же самое - к каждом числу в таблице добавляется 1. В итоге значения в таблице должны стать 4 и 5: (3,4) + 1 -> (4,5). И вот тут все не так.
Вот как выглядит вывод программы:
Как видите, последний метод почему-то не меняет данные в таблице, хотя в отладке все вроде происходит: 3 становится 4, а 4 становится 5. Но после выхода из цикла эти изменения испаряются. Такое чувство, что в этом случае создается какая-то локальная копия класса именно для цикла и изменения происходят в ней. После чего она благополучно поглощается сборщиком мусора. И главное не понятно по какой причине. Да, returning возращает value, то есть, значение, а не ссылку, но это значение и содержит ссылку. Ну, то есть, даже если это копия оригинальной ссылки, то указывать-то она все равно должна на то же самое!
В общем толи я чего-то не понимаю, толи это баг в SAP.