ABAP Trapdoors: Type Lock-In
In the past ABAP Trapdoor blogs, I’ve written about various problems that can occur when actually writing code in ABAP. Today’s issue is more about a structural or design problem. For this example, we’ll use the following class hierarchy:
Imagine a method that takes an instance of CL_SUPER to perform an arbitrary operation:
METHODS bar IMPORTING ir_object TYPE REF TO cl_super. ... DATA: lr_super TYPE REF TO cl_super, lr_sub1 TYPE REF TO cl_sub1, lr_sub2 TYPE REF TO cl_sub2. ... lr_foo->bar( lr_super ). lr_foo->bar( lr_sub1 ). lr_foo->bar( lr_sub2 ).
This is pretty straightforward – when calling this method, we can either pass a variable of TYPE REF TO cl_super or any of the subclasses because every instance of CL_SUB1 “is-a” CL_SUPER . This also holds true for subclasses that the creator of the original classes does not know about – for instance, any customer implementations that extend the existing framework.
For methods that provide EXPORTING parameters, a slightly different pattern applies:
METHODS bar EXPORTING er_object TYPE REF TO cl_super. ... DATA: lr_super TYPE REF TO cl_super, lr_root TYPE REF TO cl_root, lr_object TYPE REF TO object. ... lr_foo->bar( IMPORTING er_object = lr_super ). lr_foo->bar( IMPORTING er_object = lr_root ). lr_foo->bar( IMPORTING er_object = lr_object ).
In this case, it’s the static type of the variable that receives the exported object that matters: it may be a TYPE REF TO cl_super or any of its superclasses.
You might have guessed where this leads – for a CHANGING parameter, you have to observe the constraints that apply to IMPORTING parameters as well as those that apply to EXPORTING parameters – in other words, you can only use the exact type that is specified by the method. You cannot use any supertype because that would violate the requirement that every object passed to the method conforms at least to the structure imposed by CL_SUPER, and you cannot use a subtype either because the method is not guaranteed to return anything more specific than a reference to CL_SUPER .
Unclear on the CHANGE?
Now perhaps I’m sticking myneck out a bit far in this case, but I think that in many cases where CHANGING parameters are used for object references, they are actually unnecessary and probably used only because of a misunderstanding. Just to make it clear – to change the state of an object, you do not need to pass it as CHANGING parameter. The opposite applies as well: to make the state of an object immutable to the method you’re passing it to, it is not sufficient to declare it as IMPORTING parameter.
Let’s explore this a bit further.
METHODs print IMPORTING ir_person TYPE REF TO cl_person. ... METHOD print. DATA: l_name TYPE string, lr_nobody TYPE REF TO cl_person. l_name = ir_person-> get_name( ). WRITE: / 'Name:', l_name. * changing the reference itself is prohibited: CREATE OBJECT lr_nobody EXPORTING i_name = 'Ann Onymous'. ir_person = lr_nobody. " <--- SYNTAX ERROR * but changing the state of the referred object is allowed: ir_person->set_name( 'John Doe' ). ENDMETHOD.
The scale of the problem
Out of curiosity, I’ve checked a random system and found over 12.000 parameters of protected and public methods that use CHANGING reference parameters – so I started digging deeper. Here is what I found: