#abap
#abap
Вопрос:
Я пытаюсь вызвать поведение, описанное в документации по ключевым словам ABAP 7.50, но терпит неудачу. Это задается альтернативой 2 из CALL METHOD - dynamic_meth
:
ВЫЗОВИТЕ МЕТОД oref->(meth_name) …
Эффект
… oref может быть любой ссылочной переменной класса … это указывает на объект, который содержит метод …, указанный в meth_name . Этот метод ищется сначала в статическом типе, затем в динамическом типе oref
Я использую тестовый код, как указано ниже. Статический тип oref
— это CL1
динамический тип CL2
. Разве тогда динамический CALL METHOD
оператор не должен вызывать метод M
в CL1
?
REPORT ZU_DEV_2658_DYNAMIC.
CLASS CL1 DEFINITION.
PUBLIC SECTION.
METHODS M.
ENDCLASS.
CLASS CL1 IMPLEMENTATION.
METHOD M.
write / 'original'.
ENDMETHOD.
ENDCLASS.
CLASS CL2 DEFINITION INHERITING FROM CL1.
PUBLIC SECTION.
METHODS M REDEFINITION.
ENDCLASS.
CLASS CL2 IMPLEMENTATION.
METHOD M.
write / 'redefinition'.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA oref TYPE REF TO cl1. " static type is CL1
CREATE OBJECT oref TYPE cl2. " dynamic type is CL2
oref->m( ). " writes 'redefinition' - that's ok
CALL METHOD oref->('M'). " writes 'redefinition' - shouldn't that be 'original'?
Обновить:
Я хотел бы ответить на (первые четыре) комментарии к моему первоначальному вопросу. Из-за длинного фрагмента кода я отвечаю, дополняя свой пост, а не комментарием.
Верно, что поведение фрагмента кода исходного вопроса является стандартным поведением OO. Также верно, что для вызовов со статическим именем метода и классом типы разрешаются так, как указано в ссылке. Но тогда:
- Почему документация по ключевым словам ABAP содержит утверждение, на которое я ссылался?
- Вызовы с динамическими именами методов выполняют поиск имени метода в динамическом типе, как показано в следующем фрагменте кода. Это, конечно, не стандартное поведение OO.
Мой вопрос был: по-видимому, механизм поиска отличается от описанного. Является ли описание неправильным или я что-то пропустил?
REPORT ZU_DEV_2658_DYNAMIC4.
CLASS CL_A DEFINITION.
ENDCLASS.
CLASS CL_B DEFINITION INHERITING FROM CL_A.
PUBLIC SECTION.
METHODS M2 IMPORTING VALUE(caller) TYPE c OPTIONAL PREFERRED PARAMETER caller.
ENDCLASS.
CLASS CL_B IMPLEMENTATION.
METHOD M2.
write / caller amp;amp; ' calls b m2'.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA orefaa TYPE REF TO cl_a.
CREATE OBJECT orefaa TYPE cl_a. " static and dynamic type is CL_A
*orefaa->m2( 'orefa->m2( )' ). syntax error: method m2 is unknown'.
*CALL METHOD orefaa->('M2') EXPORTING caller = 'CALL METHOD orefa->("M2")'. results in exception: method m2 is unknown'.
DATA orefab TYPE REF TO cl_a. " static type is CL_A
CREATE OBJECT orefab TYPE cl_b. " dynamic type is CL_B
*orefab->m2( 'orefab->m2( )' ). results in syntax error: method m2 is unknown'.
CALL METHOD orefab->('M2') EXPORTING caller = 'CALL METHOD orefab->("M2")'. " succeeds
Комментарии:
1. Нет, так не должно быть. Полиморфизм по-прежнему применяется здесь независимо от того, используете ли вы статический или динамический вызов метода.
2. Статические и динамические применяются здесь скорее к статическим (классовым) методам и динамическим (ссылочным) методам.
3. Это стандартное поведение OO. По крайней мере, было бы очень странно, если бы в случае динамического вызова methid вызов происходил на более высоком уровне в иерархии классов.
4. Он соответствует другим языкам и описан в справке : каждая ссылка, указывающая на объект подкласса, использует переопределенный метод, даже если он был введен со ссылкой на суперкласс. В частности, это также относится к собственной ссылке me. … В переопределенном методе super->meth может использоваться для доступа к скрытому методу, например, для применения и дополнения его функций.
5. Понятия не имею, что означает этот текст в документации ABAP. Может быть, внутренние технические вещи, касающиеся производительности поиска? (т. Е., Если у вас есть дополнительный метод
N
CL2
, и вы делаетеCALL METHOD oref->('N')
это, сначала выполните поискN
CL1
, а поскольку он не существует, он будет затем искатьN
CL2
)
Ответ №1:
Вы на самом деле отвечаете на свой собственный вопрос, не так ли?
В вашем первом примере вы выполняете a call method
для метода m
для переменной, которая введена как cl1
. Среда выполнения ищет класс cl1
и находит m
там запрошенный метод. Затем он вызывает этот метод. Однако ваша переменная на самом деле имеет тип cl2
, подкласс cl1
, который переопределяет этот метод m
. Таким образом, вызов эффективно достигает переопределения метода, а не исходной реализации суперкласса. Как вы и комментаторы подводите итог: это стандартное объектно-ориентированное поведение.
Обратите внимание, что, по сути, это не имеет никакого отношения к оператору static-vs-dynamic, который вы цитируете из документации. Метод m
статически присутствует в cl1
, поэтому динамический поиск вообще не задействован. Я предполагаю, что вы искали способ проверить значение этого утверждения, но в этом примере он не рассматривается.
Однако ваш второй пример тогда точно попадает в точку. Позвольте мне переписать его снова с другими именами, чтобы обсудить это. Учитывая пустой суперкласс super_class
:
CLASS super_class DEFINITION.
ENDCLASS.
и подкласс sub_class
, который его наследует:
CLASS sub_class DEFINITION
INHERITING FROM super_class.
PUBLIC SECTION.
METHODS own_method.
ENDCLASS.
Теперь, как super_class
пустой, sub_class
не принимает никаких методов там. Напротив, мы добавляем метод own_method
специально для этого класса.
Следующая последовательность инструкций затем точно демонстрирует, что особенного в динамическом вызове:
DATA cut TYPE REF TO super_class.
cut = NEW sub_class( ).
CALL METHOD cut->('OWN_METHOD').
" runs sub_class->own_method
Среда выполнения обнаруживает call method
оператор. Сначала проверяется статический тип переменной cut
, который является super_class
. Запрошенный метод own_method
там отсутствует. Если бы это было все, что произошло, вызов теперь завершился бы ошибкой с исключением, не найденным методом. Если бы мы написали жестко запрограммированный cut->own_method( )
, мы бы даже не зашли так далеко — компилятор уже отклонил бы это.
Однако время call method
выполнения продолжается. Он определяет динамический тип cut
as being sub_class
. Затем он смотрит, находит ли он own_method
там. И действительно, это так. Оператор принят, и вызов направлен на own_method
. Это дополнительное усилие, которое здесь происходит, — это именно то, что описано в документации как «Этот метод ищется сначала в статическом типе, затем в динамическом типе oref«.
То, что мы видим здесь, отличается от жестко запрограммированных вызовов методов, но это также не является «незаконным». По сути, среда выполнения здесь сначала cast
приводит переменную cut
к ее динамическому типу sub_class
, а затем снова ищет доступные методы. Как если бы мы писали DATA(casted) = CAST super_class( cut ). casted->own_method( )
. Я не могу сказать, почему среда выполнения действует таким образом. Это похоже на расслабленное поведение, которое мы обычно находим в ABAP, когда операторы развиваются на протяжении всего срока их службы и должны оставаться обратно совместимыми.
Есть одна деталь, которая требует дополнительного рассмотрения: крошечное слово «тогда» в документации. Почему важно сказать, что сначала он выглядит в статическом типе, а затем в динамическом типе? В приведенном выше примере вместо этого может быть просто указано «и / или».
Почему эта деталь может быть важна, описано в моем втором ответе на ваш вопрос, который я опубликовал несколько дней назад. Позвольте мне еще раз кратко изложить это здесь, чтобы этот ответ был полным. Задан интерфейс с методом some_method
:
INTERFACE some_interface PUBLIC.
METHODS some_method RETURNING VALUE(result) TYPE string.
ENDINTERFACE.
и класс, который его реализует, но также добавляет другой собственный метод с точно таким же именем some_method
:
CLASS some_class DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES some_interface.
METHODS some_method RETURNING VALUE(result) TYPE string.
ENDCLASS.
CLASS some_class IMPLEMENTATION.
METHOD some_interface~some_method.
result = `Executed the interface's method`.
ENDMETHOD.
METHOD some_method.
result = `Executed the class's method`.
ENDMETHOD.
ENDCLASS.
Какой из двух методов теперь вызывается CALL METHOD cut->('some_method')
? Порядок в документации описывает это:
DATA cut TYPE REF TO some_interface.
cut = NEW some_class( ).
DATA result TYPE string.
CALL METHOD cut->('SOME_METHOD')
RECEIVING
result = result.
cl_abap_unit_assert=>assert_equals(
act = result
exp = `Executed the interface's method` ).
При обнаружении call method
инструкции среда выполнения сначала проверяет статический тип переменной cut
, который является some_interface
. У этого типа есть метод some_method
. Таким образом, среда выполнения продолжит вызывать этот метод. Это, опять же, стандартная ориентация объекта. Особенно обратите внимание на то, как этот пример вызывает метод some_method
, предоставляя some_method
только строку, хотя на самом деле ее полное имя some_interface~some_method
. Это согласуется с жестко запрограммированным вариантом cut->some_method( )
.
Если бы среда выполнения действовала наоборот, сначала проверяя динамический тип, а затем статический тип, она действовала бы по-другому и вместо этого вызывала собственный метод класса some_method
.
Кстати, нет способа вызвать собственный класс some_method
. Хотя в документации предполагается, что среда выполнения будет учитывать cut
динамический тип some_class
на втором шаге, также добавляется, что «В динамическом случае также возможен доступ только к компонентам интерфейса, и невозможно использовать ссылочную переменную интерфейса для доступа к компонентам любого типа».
Единственный способ вызвать собственный метод класса some_method
— это изменить cut
тип:
DATA cut TYPE REF TO some_class.
cut = NEW some_class( ).
DATA result TYPE string.
CALL METHOD cut->('SOME_METHOD')
RECEIVING
result = result.
cl_abap_unit_assert=>assert_equals(
act = result
exp = `Executed the class's method` ).
Комментарии:
1. Спасибо за ваш очень подробный ответ. Просто для справки: я в основном повторно внедряю механизм разрешения ABAP в Java. Он применяется во время компиляции и для вызова должен выдавать все целевые объекты вызова, которые могут быть выбраны во время выполнения — и только те, конечно. Ваш ответ очень помогает.
2. Ах, это объясняет тот высокий уровень детализации, который вам здесь требуется. 👍
Ответ №2:
Это скорее касается реализаций интерфейса, чем наследования классов. Что означает справка по языку ABAP, так это:
Предположим, у вас есть интерфейс, который объявляет метод
INTERFACE some_interface PUBLIC.
METHODS some_method RETURNING VALUE(result) TYPE string.
ENDINTERFACE.
и класс, который его реализует, но наряду с этим также объявляет собственный метод с тем же именем
CLASS some_class DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES some_interface.
METHODS some_method RETURNING VALUE(result) TYPE string.
ENDCLASS.
CLASS some_class IMPLEMENTATION.
METHOD some_interface~some_method.
result = `Executed the interface's method`.
ENDMETHOD.
METHOD some_method.
result = `Executed the class's method`.
ENDMETHOD.
ENDCLASS.
затем динамический вызов ссылочной переменной, введенной с интерфейсом, выберет метод интерфейса вместо собственного метода класса
METHOD prefers_interface_method.
DATA cut TYPE REF TO zfh_some_interface.
cut = NEW zfh_some_class( ).
DATA result TYPE string.
CALL METHOD cut->('SOME_METHOD')
RECEIVING
result = result.
cl_abap_unit_assert=>assert_equals(
act = result
exp = `Executed the interface's method` ).
ENDMETHOD.
На самом деле это точно такое же поведение, которое мы наблюдаем при регулярных вызовах методов, т. Е. Если мы указываем имя метода в коде, а не в переменной.
Только если среда выполнения не может найти метод с заданным именем в статическом типе, она начнет поиск метода с этим именем в динамическом типе. Это отличается от обычных вызовов методов, когда компилятор отклоняет недостающие some_interface~
и требует, чтобы мы добавили an alias
, чтобы это работало.
Кстати, как некоторые люди упоминали в комментариях, «статический» здесь не относится CLASS-METHODS
, в отличие от методов «экземпляра». «Статический тип» и «динамический тип» относятся к разным вещам, см. Раздел «Статический тип» и «Динамический тип» в справочной статье «Правила назначения ссылочных переменных«.
Комментарии:
1. Спасибо, что указали на связь с интерфейсами. Два комментария: 1) Вы говорите
Only if the runtime cannot find a method with the given name in the static type will it start looking for a method with that name in the dynamic type. This is different from regular method calls ...
, что я пытался спровоцировать такое поведение, закомментировав объявление метода вsome_interface
и его реализацию вsome_class
, но оба вызова завершаются неудачей, т. Е. Ведут себя одинаково. Не могли бы вы привести пример рабочего кода? 2) Как вы объясните поведение, продемонстрированное вторым фрагментом кода в моем исходном сообщении?2. Извините, я больше не могу редактировать свой комментарий.
provide a working code example
Я имел в виду пример кода, который демонстрирует вашу точку зрения, то есть разницу в поведении.