Skip to Content
Author's profile photo Former Member

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:

Example Class Hierarchy

IMPORTING

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.

Valid Types for IMPORTING Parameter

EXPORTING

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.

Valid Types for EXPORTING Parameter

CHANGING

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 .

Valid Types for CHANGING Parameter

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:

Evaluation of classes used in CHANGING parameters

Assigned Tags

      12 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Mark Teichmann
      Mark Teichmann
      These are very interesting questions that most developers would never ask (including me).

      Author's profile photo Kumud Singh
      Kumud Singh
      Hi,
      I read the blog twice but I still think I have only got the crux superficially pertaining to import,export and changing paramter of a class hierarchy.
      Unless I code something this would not get fixed inside.
      Any hints on how do I code it and see the happenings myself. Is this anyway related to narrow casting and broad casting concept as well?

      Regards,
      Kumud

      Author's profile photo Former Member
      Former Member
      Blog Post Author
      Kumud,

      you're right, this can be tricky to understand unless you're used to thinking in classes. I don't have a real-world example at hand, so I'd recommend to simply create the class hierarchy I've drawn (with global Z classes or local classes) and just play around with it. Note that this isn't a "trapdoor" like the other ones - no unexpected behavior of valid syntax, but rather "why wouldn't the system allow me to pass my reference here?"...

      HTH
        Volker

      Author's profile photo Uwe Fetzer
      Uwe Fetzer
      Very helpful (as usual!)
      Author's profile photo Fábio Luiz Esperati Pagoti
      Fábio Luiz Esperati Pagoti
      In Java, when it's necessary to avoid an object from being modified, interface 'clonable' can be implemented. This interface helps to clone an instance and then you can work with the copy instead of using the original object.

      Maybe this idea could be applied in ABAP in some situations.

      Author's profile photo Former Member
      Former Member
      Blog Post Author
      Fabio,

      whether an object is clonable is not related to whether it is immutable (either globally or using an interface restricted to read-only operations). You're right - it can be helpful to be able to clone an object that cannot be changed in a given context, but that's the second step at best. And there's a good reason why not every object is clonable by default.

      There is limited support in ABAP for this. The Object Services for example provide an interface IF_OS_CLONE. However, cloning can be really tricky, especially when multiple objects are involved (Clone the object itself, or all contained objects, or all referenced objects? In ABAP, there's no difference between a reference and a containment relationship.) or other, non-OO stuff comes in (What about locking? Clone the business object - for example the material - as well - that would be a copy operation. Or clone only the access object - then you'll get two instances pointing at the same business data, which can be helpful, but can also be really error-prone.)

        Volker

      Author's profile photo Uwe Fetzer
      Uwe Fetzer
      Hi Fabio,
      good idea. In the meantime you can clone an object (if really needed!) by using "SYSTEM-CALL OBJMGR CLONE org_object TO new_object."
      Uwe
      Author's profile photo Fábio Luiz Esperati Pagoti
      Fábio Luiz Esperati Pagoti
      Hi Uwe! I really didn't know about that. However, checking the help for this command I wouldn't use it as it's only for Internal use in SAP Basis development.

      http://help.sap.com/abapdocu_70/en/ABAPSYSTEM-CALL_OO.htm

      Also, having this kind of interface, enables you to create a custom logic when cloning objects. It's not always that a clone have all attributes equal it's original (this makes the name 'clonable' weird.. but anyway)

      Thanks for sharing it.

      Author's profile photo Former Member
      Former Member
      Blog Post Author
      *sigh* Something has apparently eaten up a lengthy comment reply I wrote this morning. To summarize:
      - cloning and immutability are two entirely different things
      - cloning is not always a good idea (do you really want two instances of a class that point to the same customer or material)
      - always consider what to do about the references when cloning - containment? bi-directional references?
      - the Object Services already provide IF_OS_CLONE

      @Peter: Using internal system calls is stronlgy discoured for very good reasons and in this case a perfect display of irresponsibility. I'd refuse to maintain the applications of a programmer who uses this stuff (unless I'd work for the ABAP core team, of course :-)).

      Author's profile photo Uwe Fetzer
      Uwe Fetzer
      @Volker: you are absolutly right regarding system calls (therefor I wrote the "if really needed!" part).
      But actually don't know how else I could clone an object. I've used the interface too, with the system-call in the method if_os_clone~clone.
      Author's profile photo Former Member
      Former Member
      Hello Volker,
      I wish you could blog more frequently 🙂
      Cheers,
      Suhas
      Author's profile photo Alejandro Bindi
      Alejandro Bindi
      As always, your blog raises an important point.
      Having worked myself in ISH*med, I also witnessed the overuse of the CHANGING cr_errorhandling across all the system. To worsen it, in all your custom developments you have to go along with it and do the same thing, to respect the general concept.

      I realized about this CHANGING limitation when starting in the project. I had some ideas to extend CL_ISHMED_ERRORHANDLING with things specific to the custom developments I was doing, but quickly realized that I was forced to create and narrow cast the instance outside of each method call, so I discarded the idea.

      I ended up creating a macro with this:

        clear e_rc.

        if cr_errorhandler is not bound.
          create object cr_errorhandler.
        endif.

      So at least the methods got less cluttered.

      I agree on the general misunderstanding about changing the state of an object versus changing an object reference. This is not exclusive to ABAP world but happens in Java as well (interesting discussions at http://www.yoda.arachsys.com/java/passing.html and http://javadude.com/articles/passbyvalue.htm)

      Regards