In this short post, I will try to explain “friend class” in Abap.
Normally in object oriented programing, visibility of properties of a class is limited with three scopes, Public, protected and private.
Public is open to all from anywhere.
Protected is open only to subclasses.
Private is only for classes itself. Normally can not be reached from outside of class.
But, with Friend classes you can reach even private properties of an object with out inheritance. That becomes useful when you want to implement factory pattern or in unit testing(Check comments, there is a comment from Paul Hardy!). Please check examples below;
Super class ,ZBMLCL_C_SUPER ,where you define friend classes. In this example only ZBMLCL_C_SUPERS_FRIEND is defined. You can define friend classes from form interface of class too.
CLASS ZBMLCL_C_SUPER DEFINITION
PUBLIC
CREATE PUBLIC
GLOBAL FRIENDS ZBMLCL_C_SUPERS_FRIEND "That means, ZBMLCL_C_SUPERS_FRIEND can read even private properties of this class.
.
PUBLIC SECTION.
DATA: SUPER_DATA_PUBLIC TYPE CHAR1.
METHODS RETURN_VALUE_PUBLIC RETURNING VALUE(RES) TYPE CHAR1.
PROTECTED SECTION.
DATA: SUPER_DATA_PROTECTED TYPE CHAR1.
METHODS RETURN_VALUE_PROTECTED RETURNING VALUE(RES) TYPE CHAR1.
PRIVATE SECTION.
DATA: SUPER_DATA_PRIVATE TYPE CHAR1.
METHODS RETURN_VALUE_PRIVATE RETURNING VALUE(RES) TYPE CHAR1.
ENDCLASS.
CLASS ZBMLCL_C_SUPER IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZBMLCL_C_SUPER->RETURN_VALUE_PRIVATE
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RES TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD RETURN_VALUE_PRIVATE.
RES = 'S'.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method ZBMLCL_C_SUPER->RETURN_VALUE_PROTECTED
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RES TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD RETURN_VALUE_PROTECTED.
RES = 'S'.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_C_SUPER->RETURN_VALUE_PUBLIC
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RES TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD RETURN_VALUE_PUBLIC.
RES = 'S'.
ENDMETHOD.
ENDCLASS.
You can define friend classes in Friends tab too.
and consuming class ZBMLCL_C_SUPERS_FRIEND ( friend of super )
As you can see in code below, without inheritance, you can use / call protected, even private methods and properties.
CLASS ZBMLCL_C_SUPERS_FRIEND DEFINITION
PUBLIC
"INHERITING FROM ZBMLCL_C_SUPER "protected methods and protected properties will be allowed with re-definition of methods
CREATE PUBLIC.
PUBLIC SECTION.
METHODS TEST_FRIEND.
ENDCLASS.
CLASS ZBMLCL_C_SUPERS_FRIEND IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_C_SUPERS_FRIEND->TEST_FRIEND
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD TEST_FRIEND.
DATA(REF_SUPER) = NEW ZBMLCL_C_SUPER( ).
REF_SUPER->SUPER_DATA_PRIVATE = 'S'. "Because this class is defined as friend in super class
data(prv) = REF_SUPER->RETURN_VALUE_PRIVATE( ).
REF_SUPER->SUPER_DATA_PROTECTED = 'S'. "Because this class is defined as friend in super class
data(prc) = REF_SUPER->RETURN_VALUE_PROTECTED( ).
REF_SUPER->SUPER_DATA_PUBLIC = 'S'. "That is just public, no need for inheritance or for friend definition
data(pub) = REF_SUPER->RETURN_VALUE_PUBLIC( ).
"ME->SUPER_DATA_PROTECTED = 'S'. "Because of inheritence
ENDMETHOD.
ENDCLASS.
As you can see in this simple example, even without inheritance you can still reach properties of friend class.
And here some links to read in depth.
https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenfriends.htm
https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abapclass_options.htm
Addition 12.10.2022 – An example of usage with factory pattern is added, in order to address the question, that came in comments from Shai.
Below there is a factory class example. An example to understand where friendship can be used, especially with instance creation limitation feature.
Pay attention to create protected and private words in classes. Because of that limitations Mail_SERVER_BASE, LINUX and WINDOWS classes’ instance can not be created publicly and needs to be created via factory class.
Base class, declares friendship to an empty interface. So any class that implements interface will have right to access private and protected members and also can create instance of subclasses.
CLASS ZBMLCL_CL_MAIL_SERVER_BASE DEFINITION
PUBLIC
CREATE PROTECTED "An Instance can not be created publicly!!!
GLOBAL FRIENDS ZBMLCL_IF_MAIL_SERVER_FRIEND
.
PUBLIC SECTION.
METHODS SEND_EMAIL
IMPORTING
IMPORT_DATA TYPE CHAR1
EXPORTING
EXPORT_DATA TYPE CHAR1 .
PRIVATE SECTION.
METHODS CREATE_INSTANCE
IMPORTING
SERVER_TYPE TYPE CHAR1
RETURNING VALUE(INSTANCE) TYPE REF TO ZBMLCL_CL_MAIL_SERVER_BASE.
ENDCLASS.
CLASS ZBMLCL_CL_MAIL_SERVER_BASE IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZBMLCL_CL_MAIL_SERVER_BASE->CREATE_INSTANCE
* +-------------------------------------------------------------------------------------------------+
* | [--->] SERVER_TYPE TYPE CHAR1
* | [<-()] INSTANCE TYPE REF TO ZBMLCL_CL_MAIL_SERVER_BASE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD CREATE_INSTANCE.
TRY.
CONSTANTS:
WINSRV TYPE STRING VALUE `ZBMLCL_IF_MAIL_SERVER_WINDOWS`,
LINSRV TYPE STRING VALUE `ZBMLCL_IF_MAIL_SERVER_LINUX`.
IF SERVER_TYPE EQ 'W'.
CREATE OBJECT INSTANCE TYPE (WINSRV).
ELSE.
CREATE OBJECT INSTANCE TYPE (LINSRV).
ENDIF.
CATCH CX_SY_CREATE_DATA_ERROR
CX_SY_CREATE_OBJECT_ERROR.
"Some error logic
ENDTRY.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_CL_MAIL_SERVER_BASE->SEND_EMAIL
* +-------------------------------------------------------------------------------------------------+
* | [--->] IMPORT_DATA TYPE CHAR1
* | [<---] EXPORT_DATA TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD SEND_EMAIL.
"raise not implemented !!!
ENDMETHOD.
ENDCLASS.
Empty interface
interface ZBMLCL_IF_MAIL_SERVER_FRIEND
public .
endinterface.
SubClasses
CLASS ZBMLCL_CL_MAIL_SERVER_WINDOWS DEFINITION
PUBLIC
INHERITING FROM ZBMLCL_CL_MAIL_SERVER_BASE
CREATE PRIVATE . "Instance creation is limited!
PUBLIC SECTION.
DATA: WIN_PUBLIC_DATA TYPE CHAR1.
METHODS SEND_EMAIL
REDEFINITION .
ENDCLASS.
CLASS ZBMLCL_CL_MAIL_SERVER_WINDOWS IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_CL_MAIL_SERVER_WINDOWS->SEND_EMAIL
* +-------------------------------------------------------------------------------------------------+
* | [--->] IMPORT_DATA TYPE CHAR1
* | [<---] EXPORT_DATA TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD SEND_EMAIL.
"Windows server logic to send email
EXPORT_DATA = 'S'.
ENDMETHOD.
ENDCLASS.
CLASS ZBMLCL_CL_MAIL_SERVER_LINUX DEFINITION
PUBLIC
INHERITING FROM ZBMLCL_CL_MAIL_SERVER_BASE
CREATE PRIVATE ."Instance creation is limited!
PUBLIC SECTION.
METHODS SEND_EMAIL
REDEFINITION .
ENDCLASS.
CLASS ZBMLCL_CL_MAIL_SERVER_LINUX IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_CL_MAIL_SERVER_LINUX->SEND_EMAIL
* +-------------------------------------------------------------------------------------------------+
* | [--->] IMPORT_DATA TYPE CHAR1
* | [<---] EXPORT_DATA TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD SEND_EMAIL.
"Linux server logic to send email
EXPORT_DATA = 'S'.
ENDMETHOD.
ENDCLASS.
Factory class that implements interface, therefore has rights to create instances.
CLASS ZBMLCL_CL_MAIL_SERVER_FACTORY DEFINITION
PUBLIC
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES ZBMLCL_IF_MAIL_SERVER_FRIEND .
CLASS-METHODS CREATE_SERVER_INSTANCE
IMPORTING
!SERVER_TYPE TYPE CHAR1
EXPORTING
SERVER TYPE REF TO ZBMLCL_CL_MAIL_SERVER_BASE.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS ZBMLCL_CL_MAIL_SERVER_FACTORY IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZBMLCL_CL_MAIL_SERVER_FACTORY=>CREATE_SERVER_INSTANCE
* +-------------------------------------------------------------------------------------------------+
* | [--->] SERVER_TYPE TYPE CHAR1
* | [<---] SERVER TYPE REF TO ZBMLCL_CL_MAIL_SERVER_BASE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD CREATE_SERVER_INSTANCE.
TRY.
CONSTANTS:
WINSRV TYPE STRING VALUE `ZBMLCL_CL_MAIL_SERVER_WINDOWS`,
LINSRV TYPE STRING VALUE `ZBMLCL_CL_MAIL_SERVER_LINUX`.
IF SERVER_TYPE EQ 'W'.
CREATE OBJECT SERVER TYPE (WINSRV).
ELSE.
CREATE OBJECT SERVER TYPE (LINSRV).
ENDIF.
CATCH CX_SY_CREATE_DATA_ERROR
CX_SY_CREATE_OBJECT_ERROR.
"Some error logic
ENDTRY.
ENDMETHOD.
ENDCLASS.
And finally instance consumer, mail sender class
CLASS ZBMLCL_CL_MAIL_SEND DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
CLASS-METHODS SEND_MAIL.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS ZBMLCL_CL_MAIL_SEND IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZBMLCL_CL_MAIL_SEND=>SEND_MAIL
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD SEND_MAIL.
"You can not directly create instances!!! Because class creation is limited!
"Error An instance of the class "ZBMLCL_CL_MAIL_SERVER_BASE" can only be
" created within the class itself or within one of its subclasses.
"DATA(MAILSERVER) = NEW ZBMLCL_CL_MAIL_SERVER_BASE( ).
"Error An instance of the class "ZBMLCL_CL_MAIL_SERVER_WINDOWS" cannot be created outside the class. -
"DATA(WINMAILSERVER) = NEW ZBMLCL_CL_MAIL_SERVER_WINDOWS( ).
ZBMLCL_CL_MAIL_SERVER_FACTORY=>CREATE_SERVER_INSTANCE(
EXPORTING
SERVER_TYPE = 'W'
IMPORTING
SERVER = DATA(SERVER)
).
SERVER->SEND_EMAIL(
EXPORTING
IMPORT_DATA = 'Z'
IMPORTING
EXPORT_DATA = DATA(SOMEDATA)
).
"If you want to use full properties of windows type
DATA:WINSRV TYPE REF TO ZBMLCL_CL_MAIL_SERVER_WINDOWS.
WINSRV ?= SERVER.
WINSRV->WIN_PUBLIC_DATA = '1'.
ENDMETHOD.
ENDCLASS.
I hope that helps you.
Please feel free to add your own sample code and examples to comments.