59 KiB
ABAP Object Orientation
💡 Note
This cheat sheet provides an overview on selected syntax options and concepts related to ABAP object orientation. It is supported by code snippets and an executable example. They are not suitable as role models for object-oriented design. Their primary focus is on the syntax and functionality. For more details, refer to the respective topics in the ABAP Keyword Documentation. Find an overview in the topic ABAP Objects - Overview.
- ABAP Object Orientation
Classes and Objects
Object-oriented programming in ABAP means dealing with classes and objects.
Objects ...
- are instances of a type. In this context, they are instances of a class. The terms object and instance are used synonymously.
- exist in the internal session of an ABAP program
Classes ...
- are templates for objects, i. e. they determine the appearance of
all instances of a class. All instances are created based on this
template (this is what is called
instantiation).
- If, for example, a vehicle represents a class, then the
instances of the class
vehiclehave the same setup. That means they all share the same kind of data like the brand, model and color or the same functionality like the acceleration. However, the values are different from instance to instance. Hence, an instance has a unique identity. For example, one instance is a red sedan of brand A having a certain acceleration; another instance is a black SUV of brand B and so on. You create an object (or instance respectively) that stands for an actual vehicle to work with in your ABAP program. - Basically, you might create any number of objects that are based on a class.
- If, for example, a vehicle represents a class, then the
instances of the class
- contain
components:
- Attributes of the objects (the data declarations)
- Methods that typically operate on this data
- Events
Creating Classes
You can either create local or global classes:
| Local classes |
|
| Global classes |
|
💡 Note
- Regarding the names of global and local classes and usage of classes in ABAP programs when, for example, calling methods, the system searches for a local class with the specific name at first. Then, if a local class with that name is not found, the systems searches for a global class.
- The class design must be done with care. If a class is only used in one program (or class), choosing a local class is enough. However, global classes must be prepared to be used anywhere. A later change of that class, especially regarding the visibility of components (see further down) or the data types of attributes that are used in other programs might cause problems.
- Apart from ADT, global classes can also be created in the ABAP Workbench (
SE80) or with transactionSE24in on-premise systems.
The basic structure of classes consists of a declaration and an implementation part that are both introduced by CLASS and ended by ENDCLASS.
Creating a Local Class
CLASS local_class DEFINITION.
"Here go the declaration for all components and visibility sections.
"You should place the declaration at the beginning of the program.
ENDCLASS.
CLASS local_class IMPLEMENTATION.
"Here go the method implementations.
"Only required if you declare methods in the DEFINITION part.
ENDCLASS.
Creating a Global Class
The code snippet shows a basic skeleton of a global class. There are further additions possible.
CLASS global_class DEFINITION
PUBLIC "Makes the class a global class in the class library.
FINAL "Means that no subclasses can be derived from this class.
CREATE PUBLIC. "This class can be instantiated anywhere it is visible.
...
"Here go the declaration for all components and visibility sections.
ENDCLASS.
CLASS global_class IMPLEMENTATION.
"Here go the method implementations.
"Only required if you declare methods in the DEFINITION part.
ENDCLASS.
💡 Note
The addition... CREATE PROTECTED.of the class declaration part means that the class can only be instantiated in methods of its subclasses, of the class itself, and of its friends. The addition... CREATE PRIVATEmeans that the class can only be instantiated in methods of the class itself or of its friends. Hence, it cannot be instantiated as an inherited component of subclasses.
Visibility of Components
In the class declaration part, you must specify (at least one of the) visibility sections to determine how to interact with the class. For example, you want to hide and thus disallow the usage of certain data. The visibility sections are as follows:
PUBLIC. |
Components declared in this section can be accessed from within the class and from outside including subclasses. |
PROTECTED. |
Components declared in this section can be accessed from within the class and from subclasses and friends - topics related to inheritance. |
PRIVATE. |
Components declared in this section can only be accessed from within the class in which they are declared and its friends. |
Creating the Visibility Sections
At least one section must be specified.
CLASS local_class DEFINITION.
PUBLIC.
"Here go the components.
PROTECTED.
"Here go the components.
PRIVATE.
"Here go the components.
ENDCLASS.
Defining Components
All components - attributes (using TYPES, DATA,
CLASS-DATA, and CONSTANTS for data types and data
objects), methods (using METHODS and CLASS-METHODS),
events EVENTS and CLASS-EVENTS as well as interfaces - are declared in the declaration part of the class. There, they must be
assigned to a visibility section.
Regarding, for example, DATA and CLASS-DATA, two
kind of components are to be distinguished:
- Instance components: Components that exist separately for each instance and can only be accessed in instances of a class.
- Static components: Components that are not specific for instances. They exist only once per class and can be accessed using the name of the class.
Attributes
- The attributes of a class mean the data objects declared within a class (or interface).
- Static
attributes
(
CLASS-DATA): Their content is independent of instances of a class and, thus, valid for all instances. As shown further down, static attributes can be accessed by using the class name without a prior creation of an instance. Note that changing an instance attribute means the change is visible in all instances. - Instance
attributes
(
DATA): Determine the instance-dependent state. The data is only valid in the context of an instance. As shown further down, instance attributes can only be accessed via an object reference variable.
💡 Note
You can declare static attributes that should not be changed usingCONSTANTSstatements. You specify the values for the constants when you declare them. Furthermore, the additionREAD-ONLYcan be used in the public visibility section as a means of securing data objects against undesired changes from outside. A change is only possible via the methods of the class or it subclasses.
Declaring attributes in visibility sections. In the snippet below, all attributes are declared in the PUBLIC. section of a local class.
CLASS local_class DEFINITION.
PUBLIC.
TYPES: some_type TYPE c LENGTH 3. "Type declaration
DATA: inst_number TYPE i, "Instance attributes
inst_string TYPE string,
dobj_r_only TYPE c LENGTH 5 READ-ONLY. "Read-only attribute
CLASS-DATA: stat_number TYPE i, "Static attributes
stat_char TYPE c LENGTH 3.
CONSTANTS: const_num TYPE i VALUE 123. "Non-changeable constant
PROTECTED.
"Here go more attributes.
PRIVATE.
"Here go more attributes.
ENDCLASS.
CLASS local_class IMPLEMENTATION.
"Here go all method implementations.
ENDCLASS.
Methods
- Are internal procedures determining the behavior of the class.
- Can access all of the attributes of a class and, if not defined otherwise, change their content.
- Have a parameter interface (also known as signature) with which methods can get values when being called and pass values back to the caller.
- Static
methods
can only access static attributes of a class and trigger static
events. You declare them using
CLASS-METHODSstatements in a visibility section. - Instance
methods
can access all of the attributes of a class and trigger all events.
You declare them using
CLASSstatements in a visibility section. Since instance methods are bound to instances, an instance of the class must first be created before using them.
Parameter Interface
In the simplest form, methods can have no parameter at all. Apart from that, methods can be defined with the following parameters:
| Addition | Details |
|---|---|
IMPORTING |
Defines one or multiple parameters that are imported (or input) by the method (e. g. from another object or an ABAP program) with which the method can work. |
EXPORTING |
Defines one or multiple parameters that are exported (or output) by the method (e. g. to another object or an ABAP program). |
CHANGING |
Defines one or multiple parameters that can be both imported and exported. |
RETURNING |
Only one RETURNING parameter can be defined for methods. These methods are called functional methods. Like EXPORTING parameters, RETURNING parameters pass back values (note that the formal parameters of returning parameters must be passed by value), i. e. they are output parameters. The difference is that there can be multiple EXPORTING parameters in a method. However, RETURNING parameters simplify the syntax if there is only one value to be passed back. It shortens the method call and enables method chaining. Furthermore, functional methods can, for example, be used in arithmetic or logical expressions. In case of standalone method calls, the returned value can be accessed using the addition RECEIVING. |
RAISING |
Used to declare the class-based exceptions to handle errors. |
💡 Note
- You may find the addition
EXCEPTIONSespecially in definitions of older classes. They are for non-class-based exceptions. The exceptions are based on the system fieldsy-subrcand raised according to setting this field. You can then check the value ofsy-subrcand act accordingly. This addition should not be used in ABAP for Cloud Development.- Formal parameter versus actual parameter: You define method parameters by specifying a name with a type which can be a generic or complete type. This formal parameter includes the specification of how the value passing should happen. Parameters can be passed by reference (
... REFERENCE(param) ...; note that just specifying the parameter name... param ...- as a shorter syntax - means passing by reference by default) or by value (... VALUE(param) ...). The actual parameter represents the data object whose content is passed to or copied from a formal parameter as an argument when a procedure is called. If pass-by-reference is used, a local data object is not created for the actual parameter. Instead, the procedure is given a reference to the actual parameter during the call and works with the actual parameter itself. Note that parameters that are input and passed by reference cannot be modified in the procedure. However, the use of a reference is beneficial regarding the performance compared to creating a local data object.- Parameters can be defined as optional using the
OPTIONALaddition. In doing so, it is not mandatory to pass an actual parameter. TheDEFAULTaddition also makes the passing of an actual parameter optional. However, when using this addition, as the name implies, a default value is set.
Constructors
- Constructors are special methods that are usually used for setting a defined initial state of objects, e. g. for setting a particular starting value for attributes in an object.
- A class can only have one instance constructor and one static constructor.
- Constructors always exist implicitly in classes. However, their declaration and use is optional. If they are declared explicitly, they must consequently be implemented. Note that they are always called automatically even if not declared and implemented.
- Static constructor: Automatically called when calling a class for
the first time in an internal session. This constructor is declared
using the predefined name
class_constructoras part of aCLASS-METHODSstatement in the public visibility section. Static constructors cannot have any parameters. - Instance constructor: Automatically called when a class is
instantiated and an object is created. The constructor is declared
using the predefined name
constructoras part of aMETHODSstatement. In contrast to local classes, instance constructors must be declared in the public visibility section of global classes. They can only haveIMPORTINGparameters and exceptions. In case of exceptions, make sure that you use aTRY ... ENDTRY.block in the implementation since otherwise the instance is not created if uncaught errors occur.
Example for method definitions: The following snippet shows multiple method definitions in the public section. Most of the formal parameters of the demo methods below are defined by just using the parameter name. This means passing by reference (returning parameters require to be passed by value).
CLASS local_class DEFINITION.
PUBLIC.
METHODS: inst_meth1 IMPORTING a TYPE i "instance methods
EXPORTING b TYPE i,
inst_meth2 IMPORTING c TYPE string
inst_meth2 IMPORTING d TYPE string
RETURNING VALUE(e) TYPE string,
inst_meth3 IMPORTING f TYPE i
EXPORTING g TYPE i
CHANGING h TYPE string_table
RETURNING VALUE(i) TYPE i
RAISING cx_sy_zerodivide,
constructor IMPORTING j TYPE i. "instance constructor with importing parameter
CLASS-METHODS: stat_meth1 IMPORTING k TYPE i "static methods
EXPORTING l TYPE i,
stat_meth2, "no formal parameters
class_constructor, "static constructor
"Formal parameter definitions
stat_meth3 IMPORTING VALUE(m) TYPE i "pass by value
REFERENCE(n) TYPE i "pass by reference
o TYPE i, "same as n
"OPTIONAL/DEFAULT additions
stat_meth4 IMPORTING p TYPE i DEFAULT 123
q TYPE i OPTIONAL.
ENDCLASS.
CLASS local_class IMPLEMENTATION.
METHOD inst_meth1.
... "Here goes the method implementation.
ENDMETHOD.
... "Note that all declared methods must be implemented.
ENDCLASS.
Working with Objects and Components
Declaring reference variables: To create an object, a reference variable must be declared. Such an object reference variable is also necessary for accessing objects and their components, i. e. objects are not directly accessed but only via references that point to those objects. This reference is stored in the reference variables.
DATA: ref1 TYPE REF TO local_class,
ref2 TYPE REF TO global_class,
ref3 LIKE ref1.
Creating objects: You create an object by using the instance
operator
NEW.
In doing so, a new instance of a class is created and the reference to
it is assigned to an object reference variable. The # sign
means that the type (TYPE REF TO ...) can be derived from the
context (in this case from the type of the reference variable). You can
also omit the explicit declaration of a reference variable by declaring
a new reference variable
inline,
for example, using DATA. In this case, the name of the class
must be placed after NEW and before the first parenthesis. The NEW operator replaces the older
CREATE OBJECT
statements.
ref1 = NEW #( ). "Type derived from already declared ref1
DATA(ref2) = NEW local_class( ). "Reference variable declared inline, explicit type
"(class) specification
"Old syntax. Do not use.
"CREATE OBJECT ref3. "Type derived from already declared ref3
Assigning or copying reference variables: To assign or copy
reference variables, use the assignment
operator
=. In the example below, both reference variables have the same
type.
DATA: ref1 TYPE REF TO local_class,
ref2 TYPE REF TO local_class.
ref1 = NEW #( ).
ref2 = ref1.
Overwriting reference variables: An object reference is overwritten when a new object is created with a reference variable already pointing to an instance.
ref1 = NEW #( ).
ref1 = NEW #( ).
Keeping object references in internal tables: If your use case is to retain the object references, for example, if you create a series of objects and you want to prevent object references to be overwritten when using the same reference variable, you can put the reference variables in internal tables. The following code shows that three objects are created with the same reference variable. The internal table includes all object references and, thus, their values are retained.
DATA: ref TYPE REF TO local_class,
itab TYPE TABLE OF REF TO local_class.
DO 3 TIMES.
ref = NEW #( ).
itab = VALUE #( BASE itab ( ref ) ). "Adding the reference to itab
ENDDO.
Clearing object references: Use
CLEAR
statements to explicitly clear a reference variable.
CLEAR ref.
💡 Note
Since objects use up space in the memory, they should be cleared if they are no longer needed. The garbage collector takes over this task automatically, i. e. all objects without any reference are cleared and the memory space is released.
Accessing attributes: Instance attributes are accessed using
the object component
selector
-> via a reference variable. Visible static attributes are
accessed using the class component
selector
=> via the class name. You can also declare data objects and
types by referring to the static attributes.
"Accessing instance attribute via a reference variable
... ref->some_attribute ...
"Accessing static attributes via the class name
... local_class=>static_attribute ...
"Without the class name only within the class
... static_attribute ...
"Type and data object declarations
TYPES some_type LIKE local_class=>some_attribute.
DATA dobj1 TYPE local_class=>some_type.
DATA dobj2 LIKE local_class=>some_attribute.
Calling methods: Similar to accessing attributes, instance
methods are called using -> via a reference variable. Static
methods are called using => via the class name. When used
within the class in which it is declared, the static method can also be
called without class_name=>.... You might also see method
calls with CALL METHOD
statements which are not used here (however, these statements are the
only option in the context of dynamic programming.
When methods are called, the parameters must be specified within the
parentheses.
Examples for instance method calls and static method calls:
"Calling instance methods via reference variable
ref->inst_meth( ... ).
"Calling static methods via/without the class name
class_name=>stat_meth( ... ).
"Only within the program in which it is declared.
stat_meth( ... ).
"Calling (static) methdod having no parameter
class_name=>stat_meth( ).
"Calling (static) methods having a single importing parameter:
"Note that in the method call, the caller exports values to the
"method having importing parameters defined; hence, the ABAP word
"EXPORTING is relevant. The three following method calls are the same
"Explicit use of EXPORTING.
class_name=>meth( EXPORTING a = b ).
"Only importing parameters in the signature: explicit EXPORTING not needed
class_name=>meth( a = b ).
"Only a single value must be passed: parameter name (a) and EXPORTING not needed
stat_meth( b ).
"Calling (static) methods having importing/exporting parameters
"Parameters must be specified if they are not marked as optional
class_name=>meth( EXPORTING a = b c = d "a/c: importing parameters
IMPORTING e = f ). "e: exporting parameter
"If f is not yet available, you could also declare it inline.
class_name=>meth( EXPORTING a = b c = d
IMPORTING e = DATA(f) ). "f receives type of e
"Calling (static) methods having a changing parameter;
"should be reserved for changing an existing local variable and value
DATA h TYPE i VALUE 123.
class_name=>meth( CHANGING g = h ).
"Calling (static) methods having a returning parameter.
"Basically, they do the same as methods with exporting parameters
"but they are way more versatile and you save lines of code.
"They do not need temporary variables.
"In the example, the return value is stored in a variable declared inline.
"i and k are importing parameters
DATA(result) = class_name=>meth( i = j k = l )
"They can be used with other statements, e. g. logical expressions.
IF class_name=>meth( i = j k = l ) > 100.
...
ENDIF.
"They enable method chaining.
"The example shows a method to create random integer values.
"The methods have a returning parameter.
DATA(random_no) = cl_abap_random_int=>create( )->get_next( ).
"Receiving parameter: Available in methods defined with a returning parameter;
"used in standalone method calls only.
"In the snippet, m is the returning parameter; n stores the result.
class_name=>meth( EXPORTING i = j k = l RECEIVING m = DATA(n) ).
Self-Reference me
When implementing instance methods, you can make use of the implicitly available object reference variable me which is always available and points to the respective object itself. You can use it to refer to components of the instance of a particular class but it is not needed:
... some_method( ... ) ...
... me->some_method( ... ) ...
However, if you want to access attributes of the particular class and these attributes have identical names as local attributes within the method, you can make use of me to access the attributes that are outside of the method and within the class. The following example demonstrates the use of me in a method implementation.
METHOD me_ref.
DATA str TYPE string VALUE `Local string`.
DATA(local_string) = str.
"Assuming there is a variable str declared in the class declaration part.
DATA(other_string) = me->str.
ENDMETHOD.
Notes on Inheritance
- Concept: Deriving a new class (i. e. a subclass) from an existing one (superclass) to share common components between classes. In doing so, you create hierarchies of classes (an inheritance tree) while a superclass includes components that are shared by all subclasses to provide a better structure for your code.
- Subclasses ...
- inherit all components from superclasses
- can access instance components from superclasses
- can be made more specific by declaring new components and redefining instance methods (i. e. replacing the implementations of inherited methods).
- can access static components but not redefine them.
- can only handle components in the
PROTECTEDandPUBLICsection of superclasses. - know their direct superclass but they do not know which classes inherit from them. [Note:] Handle component definitions and implementations in the superclass with great care since a change might have undesired consequences for the subclasses.
- Components ...
- that are changed and added to subclasses are not visible to superclasses, hence, these changes are only relevant for this class itself and its subclasses.
- that are added should have a different name than those of the superclass.
- A class ...
- can only inherit from one superclass, i. e. a subclass can only have one superclass, however, a superclass can have any number of subclasses.
- must enable derivation, i. e. classes cannot inherit from
classes that are specified with the addition
FINAL(e. g.CLASS global_class DEFINITION PUBLIC FINAL CREATE PUBLIC. ...).
Excursion: ABSTRACT and FINAL
- A global class declared with the addition
FINALrules out inheritance. The additionFINALis also available for method declarations in classes which rules out that such a method is redefined in subclasses. In classes that are declared withFINAL, all methods are implicitly final. Instance constructors are always final by default. - The addition
ABSTRACTis meant for abstract classes. - The addition
ABSTRACTenters the picture in the context of abstract classes. These classes are used if you want to have a template for subclasses and you do not need instances of such classes. Instances are only possible for their subclasses. Instance components of an abstract class can then be accessed via an instantiated subclass.ABSTRACTis available for class declarations (e. g.CLASS cl DEFINITION ABSTRACT. ...) and method declarations (e. g.METHODS meth ABSTRACT. ...). Abstract methods can only be declared within abstract classes. They are not implemented in the implementation part of the abstract class. Instead, they must be redefined in subclasses. Note that in abstract classes, non-abstract methods can also be declared and that private methods cannot be redefined, i. e. methods in this section cannot be declared as abstract. See a high-level comparison of abstract classes and interfaces further down.
Redefining Methods
-
The non-final method from the superclass that is redefined must be specified in the declaration part of the subclass as follows:
METHODS meth REDEFINITION.It must be specified with the same method name and in the same visibility section as in the superclass. Specifying or changing the signature is not possible.
-
If you want to access the original method in the superclass within the method implementation of the subclass, use the pseudo reference
super->....
💡 Note
If the instance constructor is implemented in a subclass, the instance constructor of the superclass must be called explicitly usingsuper->constructor, even if the latter is not explicitly declared. An exception to this: Direct subclasses of the root node object.
Notes on Polymorphism and Casting
The object orientation concept polymorphism means accessing different methods in different objects and with different behavior via the same interface, i. e. you can use one and the same reference variable to access various objects, for example, references to a superclass can point to objects of a subclass.
Note the concept of static and dynamic type in this context:
- Object reference variables (and also interface reference variables as outlined further down) have both a static and a dynamic type.
- When declaring an object reference variable, e. g.
DATA oref TYPE REF TO cl, you determine the static type, i. e.clis used to declare the reference variable that is statically defined in your program. The dynamic type is determined at runtime of the program and is the class of an object. Especially in the context of assigning object or interface references (and also data references), this differentiation enters the picture. - The following basic rule applies: The assignment of an object or interface reference variable to another one is possible if the static type of the target reference variable is more general than or the same as the dynamic type of the source reference variable.
- If it can be statically checked that an assignment is possible
although the types are different, the assignment is done using the
assignment
operator
=that triggers an upcast automatically. - Otherwise, it is a
downcast.
Here, the assignability is not checked until runtime. The downcast
must be triggered explicitly using casting
operators,
either with the constructor
operator
CASTor the older?=, for the assignment of object or interface reference variables. - See more information in the topic Assignment Rules for Reference Variables.
✔️ Hints
- If the static type is a class, the dynamic type must be the same class or one of its subclasses.
- If the static type is an interface, the dynamic type must implement the interface.
As an example, assume there is an inheritance tree with lcl_super as the superclass and lcl_sub as a direct subclass. lcl_sub2 is a direct subclass of lcl_sub.
In the following code snippet, the rule is met since the superclass is either the same as or more generic than the subclass (the subclass has, for example, redefined methods and is, thus, more specific). Hence, the assignment of an object reference variable pointing to the subclass to a variable pointing to a superclass works. An upcast is triggered. After this casting, the type of oref_super has changed and the methods of lcl_sub can be accessed via oref_super.
oref_super = NEW lcl_super( ).
oref_sub = NEW lcl_sub( ).
"Upcast
oref_super = oref_sub.
"The casting might be done when creating the object.
DATA super_ref TYPE REF TO lcl_super.
super_ref = NEW lcl_sub( ).
Such upcasts frequently occur if you want to access objects via an
object reference variable pointing to the superclass. However, there
might also be situations when you want to do the assignment the other
way round, i. e. going from specific to more generic. In this case, a
more generic reference variable is assigned to a specific variable which
can be depicted as moving downwards in the inheritance tree concerning
the assignment. As mentioned above, a downcast must be triggered
manually. Just an assignment like oref_sub = oref_super.
does not work. A syntax error occurs saying the right-hand variable's
type cannot be converted to the left-hand variable's type.
If you indeed want to carry out this casting, you must use
CAST or ?= to overcome this syntax error (but just
the syntax error!). Note: You might also use these two
operators for the upcasts. That means, oref_super = oref_sub. has the same effect as oref_super = CAST #( oref_sub ).. This syntax is usually not necessary.
At runtime, the assignment is checked and if the conversion does not
work, you face a (catchable) runtime error. Even more so, the assignment
oref_sub ?= oref_super. does not throw a syntax error but it
does not work in this example either because it violates the rule
mentioned above (oref_sub is more specific than
oref_super). To check whether such an assignment is possible
on specific classes, you can use the predicate expression IS INSTANCE OF
or the case distinction CASE TYPE OF.
Carrying out an upcast before the downcast ensures that the left-hand
variable's type is compatible to the right-hand variable's type.
oref_super = NEW lcl_super( ).
oref_sub = NEW lcl_sub( ).
oref_sub2 = NEW lcl_sub2( ).
"Downcast resulting in an error; error is caught
TRY.
oref_sub = CAST #( oref_super ).
CATCH CX_SY_MOVE_CAST_ERROR INTO DATA(e).
...
ENDTRY.
"Working downcast with a prior upcast
oref_super = oref_sub2.
"Due to the prior upcast, the following check is actually not necessary.
IF oref_super IS INSTANCE OF lcl_sub.
oref_sub = CAST #( oref_super ).
...
ENDIF.
Working with Interfaces
Interfaces ...
- represent a template for the components in the public visibility section of classes and thus enhance the components of classes by adding components of interfaces.
- represent a means to deal with multiple inheritance in ABAP Object Orientation.
- serve the concept of polymorphism. Any number of classes can implement the same interface.
- are beneficial if you want to share and reuse common components across classes especially if those classes are not in an inheritance relationship.
- are possible as both local and global interfaces.
- are different from classes in the following ways:
- They only consist of a part declaring the components without an implementation part. The classes using the interfaces are responsible for the implementation.
- They do not include visibility sections. All interface components are public.
- No instances can be created from interfaces.
- Declarations as mentioned for classes, e. g.
DATA,CLASS-DATA,METHODS,CLASS-METHODS, are possible. Constructors are not possible.
Defining Interfaces
💡 Note
The additionDEFINITIONis not relevant here since there is no implementation part.
INTERFACE intf.
"The addition PUBLIC is for global interfaces:
"INTERFACE intf_g PUBLIC.
DATA ...
CLASS-DATA ...
METHODS ...
CLASS-METHODS ...
ENDINTERFACE.
Using Interfaces in Classes
- A class can implement multiple interfaces.
- As a prerequisite, the interfaces must be specified in the
declaration part of a class using the statement
INTERFACES. - Since all interface components are public, you must include this statement and the interfaces in the public section of a class.
- In doing so, the interface components become part of the class
itself. Methods that are specified in interfaces must be implemented
in the class unless the methods are marked as optional in the
interface using the additions
DEFAULT IGNOREorDEFAULT FAIL. Furthermore, you can specify the additionABSTRACT METHODSfor theINTERFACESstatement in the declaration part of classes followed by method names. In this case, the class need not implement the methods of the interface. The implementation is then relevant for a subclass inheriting from a superclass that includes such an interface declaration. Find more information here. - You can specify alias names for the interface components using the
statement
ALIASES ... FOR .... The components can then be addressed using the alias name everywhere (since the alias is public).
Syntax for using interfaces in classes:
CLASS class DEFINITION.
PUBLIC SECTION.
INTERFACES intf1.
INTERFACES intf2 ABSTRACT METHODS meth1. "No implementation required for meth1
INTERFACES intf3 ALL METHODS ABSTRACT. "All methods abstract
ALIASES meth_alias FOR intf1~some_method.
ENDCLASS.
CLASS class IMPLEMENTATION.
METHOD intf1~some_meth. "Method implementation using the original name
...
ENDMETHOD.
"Just for demo purposes: Method implementation using the alias name
"METHOD meth_alias.
" ...
"ENDMETHOD.
...
ENDCLASS.
Accessing Interface Components
- Interface components can be addressed using the interface component
selector:
... intf~comp ....- Note: Due to the unique interface name and the use of
the name as prefix (e. g.
intf~) that is followed by a component name, there is no problem regarding the component naming within a class, i. e. the class can have components with the same name, and other interfaces can have components with the same name, too.
- Note: Due to the unique interface name and the use of
the name as prefix (e. g.
- You can then access them either using class references or interface
references:
- Class references:
... class_ref->intf~comp ... - Interface references:
... i_ref->comp .... In this case, the object component selector is used to access the components without the interface name.
- Class references:
Before making use of interface references, an interface reference
variable
must be created: DATA i_ref TYPE REF TO intf. Interfaces
cannot be instantiated, i. e. an object cannot be created, however,
interface references can point to the objects of any class that includes
the interface so that interface components (and only them) can be
accessed via the variable. Accessing an object via an interface
reference variable is basically the same as accessing a subclass object
via a superclass reference variable.
Assigning Interface Reference Variables
As mentioned above, interface references can point to the objects of any class that includes the interface. This is true when object references are assigned to interface references. Here, as touched on before, the concept of upcasting enters the picture.
DATA i_ref TYPE REF TO intf.
DATA cl_ref TYPE REF TO class.
cl_ref = NEW #( ).
"Upcast
i_ref = cl_ref.
"Method call using the interface reference variable
i_ref->some_method( ... ).
The other way round, i. e. the assignment of an interface reference to
an object reference, is also possible
(downcast
or narrowing cast). However, as mentioned before, this assignment can be
problematic since a successful assignment is dependent on whether the
object the interface reference points to is actually an object of the
implementing class. If this is not the case, a runtime error occurs. You
can carry out a downcast using the casting operator
CAST
or the operator
?=.
The example shows the use of an IS INSTANCE OF
expression to prevent a runtime error as also shown before.
DATA i_ref TYPE REF TO intf.
DATA cl_ref TYPE REF TO class.
cl_ref = NEW #( ).
IF i_ref IS INSTANCE OF class.
cl_ref = CAST #( i_ref ).
...
ENDIF.
✔️ Hints
Interfaces versus abstract classes
Coming back to abstract classes in the context of interfaces. Like interfaces, abstract classes cannot be instantiated - only their subclasses. The following list includes differences between abstract classes and interfaces that should be considered when creating them:
- Abstract classes can have components (including abstract and non-abstract methods) in other visibility sections than only public.
- Multiple inheritance is impossible with abstract classes since there can be only one abstract class as superclass.
- Non-abstract methods in abstract classes can be implemented whereas interfaces do not allow any method implementations.
Further Concepts
Factory Methods
A factory method is relevant if you want to restrict and control the instantiation of a class by external users of this class. Still, the users should be able to work with objects of the class. This is true for cases when, for example, there must be only a single object of a class (a singleton) or certain checks must be carried out before a class can be instantiated so as to guarantee a consistent creation of all objects.
A (static) factory method implemented in such a class does the trick: It creates an object of the class and returns a reference to the object.
Example for a class and factory method:
"Addition CREATE PRIVATE
CLASS class DEFINITION CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS factory_method
IMPORTING ...
RETRUNING VALUE(obj) TYPE REF TO class. "Returns an object
ENDCLASS.
...
"Calling a factory method.
DATA obj_factory TYPE REF TO class.
obj_factory = class=>factory_method( ... ).
Friendship
Classes can grant friendship to other classes and interfaces to enable
the access to protected and private components. However, the friendship
is not reciprocal. If class a grants friendship to class b, class b must
also explicitly grant friendship to class a if the component should be
made accessible also the other way round.
Friends of a class can create instances of the class without any restrictions. They are not automatically made friends of the subclasses of the class.
Friendship prevents that the components are made available to all users. A typical use case for friendship between classes is unit tests, i. e. friendship is granted to test classes so that they can access and test private components, too.
You specify the befriended class in the definition part:
CLASS class DEFINITION FRIENDS other_class.
...
Events
Events
are components of classes that can be triggered by methods. If an event
is raised (by a RAISE EVENT
statement), specific event handler
methods
are called to react on the event. The following points are relevant for
raising events:
- Defining events
- Events must be defined in a visibility section of the
declaration part of a class or in an interface, e. g. as
instance (using an
EVENTSstatement) or static events (CLASS-EVENTS). - Similar to methods, static events can be triggered by instance and static methods, instance events can only be triggered by instance methods.
- Events allow exporting parameters to be defined. They must be
passed by value. Each instance event also includes the implicit
output parameter
senderrepresenting an object reference variable for the instance for which the event is defined.
- Events must be defined in a visibility section of the
declaration part of a class or in an interface, e. g. as
instance (using an
- Defining and implementing event handler methods. These methods are defined with a special syntax:
CLASS-METHODS handler_meth FOR EVENT evt OF class
...
-
Registering event handler methods
- Event handler methods must be registered using a
SET HANDLERstatement at runtime so that they can handle a raised event at all and react accordingly. - You can register instance events for a specific instance or for
all instances of a class. Static events are registered to the
whole class without any addition to the
SET HANDLERstatement.
- Event handler methods must be registered using a
SET HANDLER handler_meth FOR ref. "Specific instance
SET HANDLER handler_meth FOR ALL INSTANCES. "All instances
SET HANDLER handler_meth. "For static events
Executable Example
Note the steps outlined here about how to import and run the code.