Skip to Content
Technical Articles
Author's profile photo Beyhan MEYRALI

Abap Friend Class

Hi,

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.

Thanks for reading.

 

Assigned Tags

      11 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Shai Sinai
      Shai Sinai

      Any details about the use cases?

      When/Why to use it?

      Author's profile photo Tudor Riscutia
      Tudor Riscutia

      Nice explanation Beyhan!

      Shai, to your question, a use case example can be a builder class.

      Author's profile photo Beyhan MEYRALI
      Beyhan MEYRALI
      Blog Post Author

      Thanks Tudor.

      Author's profile photo Shai Sinai
      Shai Sinai

      Thanks.

      I'm aware of some good use cases.

      I suggested to add some info regarding it so the readers may better understand when/what it is useful for.

      Beyhan MEYRALI , Thanks for updating the post.

      Author's profile photo Beyhan MEYRALI
      Beyhan MEYRALI
      Blog Post Author

      Hi Shai,

      I have added an example to post. Please check.

      Hopefully it is helpful for you.

      Author's profile photo Matthew Billingham
      Matthew Billingham

      Factory pattern - to ensure that only the factory can create instances of the object.

      Author's profile photo Paul Hardy
      Paul Hardy

      Some purists do not like the FRIEND concept because it breaks the whole OO concept of "data hiding" that is if you define something as private you want it to be be private.

      In unit testing you can have an "injector" class which is only active in development and which is a friend of the factory class. Thus the injector class can inject fake test doubles into the private attributes of the factory class, so when the production code is tested in development the fake test doubles are used instead of real classes.That sounds horribly complicated, but there is a load of information available about unit testing on the internet, not that anyone is much interested.

      I also use the FRIEND concept in unit testing so private methods can be tested. Some people do not like that either as they would say the reason a private method is private is so it can be changed without breaking anything external to the class even a test.

      Cheersy Cheers

      Paul

      Author's profile photo Beyhan MEYRALI
      Beyhan MEYRALI
      Blog Post Author

      Great example Paul. Thank you.

      Author's profile photo Joachim Rees
      Joachim Rees

      Thanks for the example and explanation on how FRIEND helps with UnitTesting / ABAPUnit, Paul!

      not that anyone is much interested.

      do I read a bit of frustration out of this?!  ;-|

      My feeling is: adaption of unit testing and tdd is slow in the ABAP word (around me),  but it is happening!

      best

      Joachim

      Author's profile photo Joachim Rees
      Joachim Rees

      I noticed, that I was actually MISSING an example on how exactly to define a local friend (e.g. test class) to a global class.

      SAP-Help helped me a bit: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abapclass_local_friends.htm

      But only looking at the example class CL_DEMO_AMDP_MESH mentioned there made it 'click' in my head:

      Image Description:
      ADT showing the the "Class-relevant Local Types"-Tab of class CL_DEMO_AMDP_MESH.

      The Code is:

      class cl_test_selects DEFINITION DEFERRED.
      CLASS CL_DEMO_AMDP_MESH DEFINITION LOCAL FRIENDS cl_test_selects.

      I'll keep this as a reference!

      Best
      Joachim

      ADT%20showing%20the%20the%20Class-relevant%20Local%20Types-Tab%20of%20class%20CL_DEMO_AMDP_MESH

      ADT showing the the Class-relevant Local Types-Tab of class CL_DEMO_AMDP_MESH

      Author's profile photo Joachim Rees
      Joachim Rees

      Thanks for the Blog, it's a nice reference!

      Right at the beginning, there is one piece of info that I think comes in handy: An example on how exactly FREINDS work: which class do you put it in and in wich position do you put what statement:

      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.

      -> all explained/shown in 4 Lines of code, with 1 extra comment.
      Nice!

      Thanks again
      Joachim