2009 Aug 31 12:54 AM
Hello experts,
I used static constructors in a simple scenario with one child class inheriting from a parent class. Both classes had static attributes and a static constructor defined. The following code demonstrates the scenario:
CLASS lcl_parent DEFINITION.
PUBLIC SECTION.
CLASS-METHODS class_constructor.
CLASS-DATA parent_attribute TYPE string VALUE `Parent`.
ENDCLASS.
CLASS lcl_parent IMPLEMENTATION.
METHOD class_constructor.
WRITE / `Parent class constructor executed.`.
ENDMETHOD.
ENDCLASS.
CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.
PUBLIC SECTION.
CLASS-METHODS class_constructor.
CLASS-DATA child_attribute TYPE string VALUE `Child`.
ENDCLASS.
CLASS lcl_child IMPLEMENTATION.
METHOD class_constructor.
WRITE / `Child class constructor executed.`.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
lcl_child=>parent_attribute = `any_value`. " <-- Access to child class
" does not execute child static constructor!To my surprise, the access to an attribute of the child class did not trigger an execution of the static constructor of the child class! Only the static constructor of the parent class is executed. The ABAP runtime seems to execute only the static constructor of the class where an attribute is defined in a class hierarchy.
However, as a programmer, my expectation is that my class is initialized when I use it for the first time. So in the example above, when I access a class component of my child class for the first time, I expect the class constructor of exactly this child class to be executed before this access -- regardless if I accessed an inherited attribute or not. At last, the accessed attribute belongs to my child class!
What is your opinion?
Regards
David
2009 Aug 31 1:27 PM
Hello David
Apparently you have check the following coding as well (which works):
...
START-OF-SELECTION.
lcl_child=>create( ). " static dummy method =>
" empty but triggers CLASS_CONSTRUCTOR of child
go_child ?= lcl_child=>instance. " <-- dumps, as static constructor of LCL_CHILD has
" not yet been executed and thus INSTANCE is initial!
go_child->say_foo( ).
Have a look at class CL_REMI_ADDON and see how the CLASS_CONSTRUCTOR is used (here). Transaction RECN (RE-FX transaction) calls the static method CF_RECA_BDT_FRAME=>CALL_DIALOG( 😞
METHOD call_dialog.
* software component REAL ESTATE EXTENSION IN R/3 ENTERPRISE is required
* (for further information see notes 443311 and 460524)
CALL METHOD cl_remi_addon=>assert_re_ea_fin.
...
In order to see how to use CLASS_CONSTRUCTOR please have a look at the following RE-FX example.
Calling the static method ASSERT_RE_EA_FIN triggers the CLASS_CONSTRUCTOR of CL_REMI_ADDON:
METHOD class_constructor .
* REAL ESTATE IN SAP R/3 ENTERPRISE FINANCIALS EXTENSION
* is active, if the following conditions are true:
* (1) - structure package EA-FIN is active (client-independent)
* (2) - BTE-application RE is active (client-independent)
* (3) - real estate flag EAACTIVE is set (client-dependent)
DATA:
lf_extension_active TYPE abap_bool,
lf_bte_appl_active TYPE abap_bool.
* INIT RESULTS
mf_eainst = abap_false.
mf_eaactive = abap_false.
* BODY
* (1) check structure package
* you can activate it as an application in transaction FIBF
CALL FUNCTION 'GET_R3_EXTENSION_SWITCH'
EXPORTING
i_structure_package = mc_strpack
IMPORTING
e_active = lf_extension_active
EXCEPTIONS
OTHERS = 1.
IF ( sy-subrc <> 0 ) OR ( lf_extension_active <> abap_true ).
* extension not active
RETURN.
ENDIF.
...
Finally, the problem of your coding is the type of GO_CHILD:
In the parent class the attribute INSTANCE is of TYPE REF TO lcl_parent but your GO_CHILD is of TYPE REF TO lcl_child.
Change your coding as following and it works:
...
** DATA go_child TYPE REF TO lcl_child.
DATA go_child TYPE REF TO lcl_parent.
START-OF-SELECTION.
go_child ?= lcl_child=>instance.
** go_child->say_foo( ). " <<< parent does not have this method
Regards
Uwe
2009 Aug 31 9:09 AM
Hello David
When you change your coding to:
START-OF-SELECTION.
lcl_child=>child_attribute = `any_value`.
write: / syst-uline.
** lcl_child=>parent_attribute = `any_value`. " <-- Access to child class
" does not execute child static constructor!
then both static constructors are triggered:
(1) parent
(2) child
CONCLUSION: When you access an inherited static attribute for the first time only the parental static constructor will be triggered. When you access a static attribute of the child class first the parental static constructor MUST be triggered followed by the child static constructor.
And if you change your coding like this (modify PARENT_ATTRIBUTE within both static constructors) the outcome is the same:
REPORT zus_sdn_static_constructor.
CLASS lcl_parent DEFINITION.
PUBLIC SECTION.
CLASS-METHODS class_constructor.
CLASS-DATA parent_attribute TYPE string VALUE `Parent`.
ENDCLASS. "lcl_parent DEFINITION
* CLASS lcl_parent IMPLEMENTATION
*----------------------------------------------------------------------*
CLASS lcl_parent IMPLEMENTATION.
METHOD class_constructor.
WRITE / `Parent class constructor executed.`.
parent_attribute = 'Set by parent class'.
ENDMETHOD. "class_constructor
ENDCLASS. "lcl_parent IMPLEMENTATION
CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.
PUBLIC SECTION.
CLASS-METHODS class_constructor.
CLASS-DATA child_attribute TYPE string VALUE `Child`.
ENDCLASS. "lcl_child DEFINITION
CLASS lcl_child IMPLEMENTATION.
METHOD class_constructor.
WRITE / `Child class constructor executed.`.
parent_attribute = 'Overwritten in child class'.
ENDMETHOD. "class_constructor
ENDCLASS. "lcl_child IMPLEMENTATION
DATA: go_child TYPE REF TO lcl_child.
START-OF-SELECTION.
** lcl_child=>child_attribute = `any_value`.
** write: / syst-uline.
lcl_child=>parent_attribute = `any_value`. " <-- Access to child class
" does not execute child static constructor!
WRITE: / lcl_child=>parent_attribute.
END-OF-SELECTION.
Result:
Parent class constructor executed.
any_value
Regards
Uwe
2009 Aug 31 10:14 AM
Hello Uwe,
thank you for your answer. I think I have understood the behavior of which static constructor is called when -- I just wonder if this is correct. Please have a look at the following sample with an inherited eager singleton class:
REPORT z_eager_singleton.
CLASS lcl_parent DEFINITION CREATE PROTECTED.
PUBLIC SECTION.
CLASS-METHODS class_constructor.
CLASS-DATA instance TYPE REF TO lcl_parent.
ENDCLASS.
CLASS lcl_parent IMPLEMENTATION.
METHOD class_constructor.
CREATE OBJECT instance.
ENDMETHOD.
ENDCLASS.
CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.
PUBLIC SECTION.
CLASS-METHODS class_constructor.
METHODS say_foo.
ENDCLASS.
CLASS lcl_child IMPLEMENTATION.
METHOD class_constructor.
CREATE OBJECT instance TYPE lcl_child.
ENDMETHOD.
METHOD say_foo.
WRITE `Foo.`.
ENDMETHOD.
ENDCLASS.
DATA go_child TYPE REF TO lcl_child.
START-OF-SELECTION.
go_child ?= lcl_child=>instance. " <-- dumps, as static constructor of LCL_CHILD has
" not yet been executed and thus INSTANCE is initial!
go_child->say_foo( ).This code produces a dump at the commented line: The class attribute INSTANCE of the class LCL_CHILD is initial when it is accessed. The static constructor of the child class is not executed before this statement.
From my point of view, as application programmer, I expect the ABAP runtime to initialize all used classes before the first access to one of the class attributes by calling the static constructor. Otherwise, I would have to ensure manually that the static constructor has been executed by calling an empty static dummy method (which must be named different for each subclass since ABAP OO does not allow me to redefine static methods!) -- what would be a very poor solution when using inherited classes, especially in the case of singletons.
Therefore, I ask: Is it really the intention to produce such an overhead to the application programmers? In addition, I do not see the use of calling static constructors in the way it is done currently...
Regards,
David
2009 Aug 31 1:27 PM
Hello David
Apparently you have check the following coding as well (which works):
...
START-OF-SELECTION.
lcl_child=>create( ). " static dummy method =>
" empty but triggers CLASS_CONSTRUCTOR of child
go_child ?= lcl_child=>instance. " <-- dumps, as static constructor of LCL_CHILD has
" not yet been executed and thus INSTANCE is initial!
go_child->say_foo( ).
Have a look at class CL_REMI_ADDON and see how the CLASS_CONSTRUCTOR is used (here). Transaction RECN (RE-FX transaction) calls the static method CF_RECA_BDT_FRAME=>CALL_DIALOG( 😞
METHOD call_dialog.
* software component REAL ESTATE EXTENSION IN R/3 ENTERPRISE is required
* (for further information see notes 443311 and 460524)
CALL METHOD cl_remi_addon=>assert_re_ea_fin.
...
In order to see how to use CLASS_CONSTRUCTOR please have a look at the following RE-FX example.
Calling the static method ASSERT_RE_EA_FIN triggers the CLASS_CONSTRUCTOR of CL_REMI_ADDON:
METHOD class_constructor .
* REAL ESTATE IN SAP R/3 ENTERPRISE FINANCIALS EXTENSION
* is active, if the following conditions are true:
* (1) - structure package EA-FIN is active (client-independent)
* (2) - BTE-application RE is active (client-independent)
* (3) - real estate flag EAACTIVE is set (client-dependent)
DATA:
lf_extension_active TYPE abap_bool,
lf_bte_appl_active TYPE abap_bool.
* INIT RESULTS
mf_eainst = abap_false.
mf_eaactive = abap_false.
* BODY
* (1) check structure package
* you can activate it as an application in transaction FIBF
CALL FUNCTION 'GET_R3_EXTENSION_SWITCH'
EXPORTING
i_structure_package = mc_strpack
IMPORTING
e_active = lf_extension_active
EXCEPTIONS
OTHERS = 1.
IF ( sy-subrc <> 0 ) OR ( lf_extension_active <> abap_true ).
* extension not active
RETURN.
ENDIF.
...
Finally, the problem of your coding is the type of GO_CHILD:
In the parent class the attribute INSTANCE is of TYPE REF TO lcl_parent but your GO_CHILD is of TYPE REF TO lcl_child.
Change your coding as following and it works:
...
** DATA go_child TYPE REF TO lcl_child.
DATA go_child TYPE REF TO lcl_parent.
START-OF-SELECTION.
go_child ?= lcl_child=>instance.
** go_child->say_foo( ). " <<< parent does not have this method
Regards
Uwe
2009 Sep 01 10:19 AM
Hello Uwe,
thank you for your answer. By reading it, I found out the reason for my misunderstanding: In ABAP, static fields of the superclass are shared with the subclasses. I have experience from Java, where static fields are really inherited (i.e. each subclass has its own copy of the static field).
In this context I understand why the static constructor of the subclass is not called when a static field of the superclass is accessed. It actually is a feature, not a bug.
However, I wonder more and more why SAP reduced the power of the OO programming concept at some places, e.g. by the forbidden redefinition of static methods or like in this case by sharing static fields with subclasses instead of inheriting them. This makes it hard to apply popular and proven OO patterns to ABAP (like the eager creation singleton, which is maybe one of the simplest OO patterns).
Regards,
David