Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member184578
Active Contributor

There are many discussions about Widening cast in SCN. some say, it is Up casting while other say it is Down casting. I remember in a way as depicted below and still following the same.

Let's take a simple inheritance tree.

         

Narrowing Cast:

Assigning/copying the instance of sub class to super class instance is called Narrowing Cast.

As shown in the figure, assigning sub class to super class is going up wards. From sub class(es) to super class while moving Up the path becomes narrow as shown in the left image. So I simply remember this. Narrowing cast = Up cast :smile: .

Widening Cast:

Assigning/coping the instance of super class to the sub class instance is called Widening Cast.

This is the reverse of Narrowing cast. As shown in the figure, assigning super class to sub class is going down wards. From super class to sub class while moving Down the path becomes wider as shown in the left image. So I simply remember this - Widening cast = Down cast.

I will still remember the above inheritance tree while referring casting and refer Widening cast as Down casting ( though is is referred as up casting from release 7.0).

Coming to the topic, I use Widening cast in this blog (instead of up/down casting ) .

To understand the Widening cast, I will just explain with the simple program.

Simple Inheritance Report
REPORT zkk_widening_cast_demo.

CLASS lcl_parent DEFINITION.
   PUBLIC SECTION.
     METHODS parent_method.

ENDCLASS.                    "lcl_parent DEFINITION

  CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.
   PUBLIC SECTION.
     METHODS parent_method REDEFINITION.
     METHODS child_method.

ENDCLASS.                    "lcl_child DEFINITION

  CLASS lcl_parent IMPLEMENTATION.

   METHOD parent_method.
     WRITE / 'Called -> parent method in Parent Class!'.
   ENDMETHOD.                    "parent_method
ENDCLASS.                    "lcl_parent IMPLEMENTATION


CLASS lcl_child IMPLEMENTATION.

   METHOD parent_method.
     WRITE / 'Called -> parent redifinition method in Child Class!'.
   ENDMETHOD.                    "parent_method
   METHOD child_method.
     WRITE / 'Called -> child method in Child Class!'.
   ENDMETHOD.                    "child_method

ENDCLASS.                    "lcl_child IMPLEMENTATION

  START-OF-SELECTION.

   DATA: lr_parent TYPE REF TO lcl_parent,
         lr_child  TYPE REF TO lcl_child.

   CREATE OBJECT: lr_parent,lr_child.

   lr_parent->parent_method( ).
   lr_child->parent_method( ).
   lr_child->child_method( ).

Now after executing this I got the below output:

Now I am interested in child(sub) class method in my parent(super) class. Which mean when I call the 'parent_method' of parent class in the above report then it should call the 'parent_method' of child class( Redefined method call instead of original method call). So I do a Narrowing cast.

continuing the above report.

CREATE OBJECT: lr_parent,lr_child.

   WRITE / 'Before Narrowing Cast:'.
   lr_parent->parent_method( ).
   lr_child->parent_method( ).
   lr_child->child_method( ).

*  Narrowing cast
   lr_parent = lr_child.

   WRITE / 'After Narrowing Cast:'.
   lr_parent->parent_method( ).
   lr_child->parent_method( ).
   lr_child->child_method( ).

Now when I execute the above report, I get the below output, which is as expected.

Now I am interested in parent(super) class method in my child(sub) class. Which mean when I call the 'parent_method' of child class in the above report then it should call the 'parent_method' of super class( Super class method call instead of redefined sub class method call). So I do a Widening cast.

CREATE OBJECT: lr_parent,lr_child.

   WRITE / 'Before Widening Cast:'.
   lr_parent->parent_method( ).
   lr_child->parent_method( ).
   lr_child->child_method( ).

   TRY .
*   Widening Cast
     lr_child ?= lr_parent.
    
     WRITE / 'After Widening Cast:'.
     lr_parent->parent_method( ).
     lr_child->parent_method( ).
     lr_child->child_method( ).
   CATCH cx_sy_move_cast_error.
     WRITE / 'Widening Cast Failed!'.
   ENDTRY.

The output is as below:

After seeing the above output, we remember that " It is always not possible to do Widening cast as the sub class will have more functionality compared to super class" .

So, I removed the method definition 'child_method' in the child class and tried Widening cast. I got the same output!

I created an empty class definitions and tried widening cast to see the widening cast success message :razz: .

CLASS lcl_parent DEFINITION.

ENDCLASS.                    "lcl_parent DEFINITION


CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.

ENDCLASS.                    "lcl_child DEFINITION


CLASS lcl_parent IMPLEMENTATION.

ENDCLASS.                    "lcl_parent IMPLEMENTATION


CLASS lcl_child IMPLEMENTATION.

ENDCLASS.                    "lcl_child IMPLEMENTATION


START-OF-SELECTION.

   DATA: lr_parent TYPE REF TO lcl_parent,
         lr_child  TYPE REF TO lcl_child.

   CREATE OBJECT: lr_parent,lr_child.

   TRY .
*   Widening Cast
     lr_child ?= lr_parent.
     WRITE / 'Widening Cast Success!'.
   CATCH cx_sy_move_cast_error.
     WRITE / 'Widening Cast Failed!'.
   ENDTRY.

Now we will see the output which we are all waiting for!

disappointed?  :smile:

The above example was taken from one of the tweets of  se38 and is the motivation for writing this blog.

The Widening cast will always fail unless the assigning instance has the same type of instance to which we are assigning.

So people used to tell do Narrowing cast before Widening Cast!, to have the assigning instance same reference.

I will take the main report and do a narrowing cast before widening cast.

REPORT zkk_widening_cast_demo.

CLASS lcl_parent DEFINITION.
   PUBLIC SECTION.
     METHODS parent_method.

ENDCLASS.                    "lcl_parent DEFINITION


CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.
   PUBLIC SECTION.
     METHODS parent_method REDEFINITION.
     METHODS child_method.

ENDCLASS.                    "lcl_child DEFINITION


CLASS lcl_parent IMPLEMENTATION.

   METHOD parent_method.
     WRITE / 'Called -> parent method in Parent Class!'.
   ENDMETHOD.                    "parent_method
ENDCLASS.                    "lcl_parent IMPLEMENTATION


CLASS lcl_child IMPLEMENTATION.

   METHOD parent_method.
     WRITE / 'Called -> parent redifinition method in Child Class!'.
   ENDMETHOD.                    "parent_method
   METHOD child_method.
     WRITE / 'Called -> child method in Child Class!'.
   ENDMETHOD.                    "child_method

ENDCLASS.                    "lcl_child IMPLEMENTATION


START-OF-SELECTION.

   DATA: lr_parent TYPE REF TO lcl_parent,
         lr_child  TYPE REF TO lcl_child.

   CREATE OBJECT: lr_parent,lr_child.

   WRITE / 'Before Widening Cast:'.
   lr_parent->parent_method( ).
   lr_child->parent_method( ).
   lr_child->child_method( ).

   lr_parent = lr_child. " Narrowing cast

   TRY .
*   Widening Cast
     lr_child ?= lr_parent.
     WRITE / 'After Widening Cast:'.
     lr_parent->parent_method( ).
     lr_child->parent_method( ).
     lr_child->child_method( ).
   CATCH cx_sy_move_cast_error.
     WRITE / 'Widening Cast Failed!'.
   ENDTRY.

Now lr_parent has same type of lr_child as we did Narrowing cast. We will see the output:

Wow! widening cast is successful this time. But we can see that this output is same as the output of Narrowing cast shown above (which is not what we required).

Because, we have copied the lr_child instance to lr_parent ( lr_parent = lr_child " Narrow cast) before widening cast. Now lr_parent has same type of lr_child and we assign lr_child ?= lr_parent which is noting but copying child class instance to child class instance!

So, when this Widening cast is actually useful?

To explain this, I will take a simple example using interfaces:

REPORT zkk_widening_cast_demo.

INTERFACE lif_test.
   METHODS interface_method.
ENDINTERFACE.                    "lif_test

CLASS lcl_myclass DEFINITION.
   PUBLIC SECTION.
     INTERFACES lif_test.
ENDCLASS.                    "lcl_class1 DEFINITION

CLASS lcl_myclass IMPLEMENTATION.
   METHOD lif_test~interface_method.
     WRITE / 'Interface method call in My Class'.
   ENDMETHOD.                    "lif_test~interface_method
ENDCLASS.                    "lcl_class1 IMPLEMENTATION


START-OF-SELECTION.

   DATA: lr_intf TYPE REF TO lif_test,
         lr_mine TYPE REF TO lcl_myclass.

   CREATE OBJECT lr_intf TYPE lcl_myclass.
   TRY .
*   Widening Cast
       lr_mine ?= lr_intf.
       WRITE /'Widening Cast Successful!'.
     CATCH cx_sy_move_cast_error.
       WRITE / 'Widening Cast Failed!'.
   ENDTRY.

Output:

Since we cannot directly assign the interface reference to class reference,

As using, lr_mine = lr_intf   " will give syntax error because lr_intf is not the same type of lr_mine.

So we do a Widening cast, lr_mine ?= lr_intf , which won't give syntax check and at run time if lr_intf contains the same reference of lr_mine, the reference will be copied else the exception will be thrown.

Now lets take one more class to understand better.

REPORT zkk_widening_cast_demo.

INTERFACE lif_test.
   METHODS interface_method.
ENDINTERFACE.                    "lif_test

CLASS lcl_myclass DEFINITION.
   PUBLIC SECTION.
     INTERFACES lif_test.
ENDCLASS.                    "lcl_class1 DEFINITION

CLASS lcl_yourclass DEFINITION.
   PUBLIC SECTION.
     INTERFACES lif_test.
ENDCLASS.                    "lcl_class2 DEFINITION

CLASS lcl_myclass IMPLEMENTATION.
   METHOD lif_test~interface_method.
     WRITE / 'Interface method call in My Class'.
   ENDMETHOD.                    "lif_test~interface_method
ENDCLASS.                    "lcl_class1 IMPLEMENTATION

CLASS lcl_yourclass IMPLEMENTATION.

   METHOD lif_test~interface_method.
     WRITE / 'Interface method call in Your class'.
   ENDMETHOD.                    "lif_test~interface_method
ENDCLASS.                    "lcl_class2 IMPLEMENTATION


START-OF-SELECTION.

   DATA: lr_intf TYPE REF TO lif_test,
         lr_mine TYPE REF TO lcl_myclass,
         lr_your TYPE REF TO lcl_yourclass.

*  CREATE OBJECT lr_intf TYPE lcl_myclass.
   CREATE OBJECT lr_intf TYPE lcl_yourclass.

    TRY .
*      Widening Cast
        lr_mine ?= lr_intf.
        WRITE /'Widening Cast Successful!'.
      CATCH cx_sy_move_cast_error.
        WRITE / 'Widening Cast Failed!'.
    ENDTRY.

In this case the widening cast will be failed since lr_intf is not of same type as lr_mine.

We use widening cast mainly while dealing with interfaces and in case of dynamic programming.

Now, If we look at the below statement

CREATE OBJECT lr_intf TYPE lcl_yourclass.
    TRY .
*      Widening Cast
        lr_mine ?= lr_intf.
        WRITE /'Widening Cast Successful!'.
      CATCH cx_sy_move_cast_error.
        WRITE / 'Widening Cast Failed!'.
    ENDTRY.

is nothing but Narrowing cast before widening cast,

DATA lr_mine1 TYPE REF TO lcl_myclass.
   CREATE OBJECT lr_mine1.

   lr_intf = lr_mine1.  " Narrowing Cast
   TRY .
*      Widening Cast
       lr_mine ?= lr_intf.
       WRITE /'Widening Cast Successful!'.
     CATCH cx_sy_move_cast_error.
       WRITE / 'Widening Cast Failed!'.
   ENDTRY.

So in this context, "Do Narrowing cast before Widening cast"  makes sense without confusion!

Hope I tried to explain Widening cast to some extent!.  Comments are welcome :smile:

19 Comments