01-12-2014 3:00 AM
So I've being doing ABAP and ABAP Objects for a while now. Then I found out that accessing a protected attribute 'externally' is possible as long as you've initiated the call from inside one of its subclass.
Is anyone else surprised by this? In other languages, I don't think this is allowed. Do you think this is a good feature? Doesn't this make code less secure?
In the code below, you can see that an instance of lcl_child tried to access the protected attribute 'name' of another instance whose type is lcl_parent - a superclass of lcl_child.
REPORT YACCESSTEST.
CLASS lcl_parent DEFINITION.
PUBLIC SECTION.
METHODS constructor
IMPORTING
p_name TYPE string.
PROTECTED SECTION.
DATA name TYPE string.
ENDCLASS.
CLASS lcl_parent IMPLEMENTATION.
METHOD Constructor.
name = p_name.
ENDMETHOD.
ENDCLASS.
CLASS lcl_child DEFINITION INHERITING FROM lcl_parent.
PUBLIC SECTION.
METHODS:
Constructor,
Test
IMPORTING p_o_parent TYPE REF TO lcl_parent.
ENDCLASS.
CLASS lcl_child IMPLEMENTATION.
METHOD Constructor.
super->Constructor( 'BABY' ).
ENDMETHOD.
METHOD Test.
" Should I be able to access this 'PUBLICLY' because
" I'm inside the subclass of lcl_parent? Note that this
" can be a different instance from me.
WRITE p_o_parent->name.
ENDMETHOD.
ENDCLASS.
DATA:
o_parent TYPE REF TO lcl_parent,
o_child TYPE REF TO lcl_child.
START-OF-SELECTION.
CREATE OBJECT o_parent EXPORTING p_name = 'MOMMY'.
CREATE OBJECT o_child.
* WRITE o_parent->name. " error. not accessible!
o_child->Test( o_parent ). " outputs 'MOMMY', why possible?!?!?!?!
01-13-2014 12:46 PM
Here's an example that removes the question of inheritance.
CLASS main DEFINITION.
PUBLIC SECTION.
METHODS show IMPORTING i_ref TYPE REF TO main.
METHODS set IMPORTING i_value TYPE string.
PRIVATE SECTION.
DATA: value TYPE string.
ENDCLASS. "main DEFINITION
CLASS main IMPLEMENTATION.
METHOD show.
WRITE: i_ref->value.
ENDMETHOD. "show
METHOD set.
me->value = i_value.
ENDMETHOD. "set
ENDCLASS. "main IMPLEMENTATION
START-OF-SELECTION.
DATA: ref1 TYPE REF TO main,
ref2 TYPE REF TO main.
CREATE OBJECT: ref1, ref2.
ref1->set( 'value of ref1' ).
ref2->set( 'value of ref2' ).
ref1->show( ref2 )..
If I pass ref2 to Ref1, then methods within ref1 have access to ref2's private attributes.
I understood private attributes were private to that instance. But it appears they are only private to that class.
01-12-2014 11:16 AM
Hello Emir,
Quite an interesting question
The following things which i come to my mind right now -
Is anyone else surprised by this? In other languages, I don't think this is allowed.
Tbh i'll surprised if other programming languages do not allow it. How would the compiler determine it statically?
Doesn't this make the code less secure?
No, for me this is very well within the definitions of OOP. You cannot access the protected elements outside the inheritance tree, but inside it.
Let's see what the OO gurus, , , have to say about it.
BR,
Suhas
PS - I have no knowledge of any programming language other than ABAP, so please excuse me for my ignorance.
01-12-2014 2:53 PM
What we have here is the protected attributes of one instance being accessible from another instance.
I wouldn't expect the syntax checker to pick it up (it doesn't pick up when the constructor calls the super-constructor which then calls an overrridden method, even though this ends in a dump), but I would expect it to dump.
I'm surprised.
01-13-2014 4:57 PM
Quite surprising for sure. However the "test" method isn't something you would find inside lcl_child class. The method parameter is creating a dependency between two instances which actually might be the same.
Instances from the same class should be independent from each other (at least in 99% of the scenarios I can imagine) so you would have to transfer the "test" method to a different class (for example a writer class). As a consequence it wouldn't be possible to access the attribute without a getter or using the READ-ONLY option for the attribute name.
01-13-2014 7:30 AM
I wrote a little Java program based on your ABAP program, just to verify and the result was the same.
public class TestProtected {
public static void main(String[] args) {
Parent p = new Parent("mommy");
Child c = new Child();
c.test(p);
}
}
class Parent {
protected String name;
public Parent(String n) {
name = n;
}
}
class Child extends Parent{
public Child(){
super("baby");
}
public void test(Parent p){
System.out.println(p.name); //Prints Mommy
System.out.println(super.name); // Prints Baby
}
}
The checks at runtime must only based on the class definitions, not the specific instance.
Must admit I was suprised about this. I would not write anything in this way, but nevertheless good to know.
01-13-2014 8:43 AM
Hello Katan,
Thanks for the Java program
The checks at runtime must only based on the class definitions, not the specific instance.
Did you mean runtime or design time?
I would not write anything in this way, but nevertheless good to know.
Ditto.
Tbh i was not surprised at the behavior of the compiler/runtime environment, but Matt & you seemed to be so Am i missing something?
BR,
Suhas
01-13-2014 8:45 AM
Wrote the same in PHP... and it worked too!!! PHP code- 24 lines - codepad
NOOOoo!!! I don't know what to believe in anymore... Is the world flat? Did Neil Armstrong really land on the moon? lol
01-13-2014 8:53 AM
Is the world flat?
The world is not flat, for sure!
Did Neil Armstrong really land on the moon? lol
Now that's debatable
01-13-2014 11:06 AM
Hi,
There are two instance having same variable "name"
each instance has unique values saved
i.e.
o_parent : name = MOMMY
o_child : name = BABY
while calling a method Test and pass ref. of o_parent
it will simply represent parent's instance and show a value of MOMMY.
*i'm just beginner in OO if any correct me..
-Avirat
01-13-2014 11:42 AM
Ehm. I do not see any problem or strange behavior in your example
You can see output of protected attribute just because you acces it inside class (which is ok).
METHOD Test.
WRITE p_o_parent->name. "<- here
ENDMETHOD.
Try this. It wont work same like parent acces of protected attribute:
WRITE o_child->name. "name will still not be accesible outside of class
Private attribute is accesible in class only.
Protected attribute is accesible in class and in its subclass.
Public is accesible in class, sublass and outside of class.
01-13-2014 12:46 PM
Here's an example that removes the question of inheritance.
CLASS main DEFINITION.
PUBLIC SECTION.
METHODS show IMPORTING i_ref TYPE REF TO main.
METHODS set IMPORTING i_value TYPE string.
PRIVATE SECTION.
DATA: value TYPE string.
ENDCLASS. "main DEFINITION
CLASS main IMPLEMENTATION.
METHOD show.
WRITE: i_ref->value.
ENDMETHOD. "show
METHOD set.
me->value = i_value.
ENDMETHOD. "set
ENDCLASS. "main IMPLEMENTATION
START-OF-SELECTION.
DATA: ref1 TYPE REF TO main,
ref2 TYPE REF TO main.
CREATE OBJECT: ref1, ref2.
ref1->set( 'value of ref1' ).
ref2->set( 'value of ref2' ).
ref1->show( ref2 )..
If I pass ref2 to Ref1, then methods within ref1 have access to ref2's private attributes.
I understood private attributes were private to that instance. But it appears they are only private to that class.
01-13-2014 1:14 PM
"value" is private for class not for each object (ref1, ref2..). So ref1 value is visible from ref2 instance.
Info from SAP Help on "PRIVATE SECTION" keyword:
Hope this helps understanding
01-13-2014 2:04 PM
Well - it's nice to learn something new. It seems common across OO languages, but still seems a bit weird.
01-13-2014 5:00 PM
Nice example Matthew!
A misunderstanding could be solved making "show" a static method.
01-14-2014 4:11 AM
Glad to know I'm not the only one
I've always thought of instance->attribute as a public way of accessing regardless of where the call has been made since you are essentially accessing from the 'outside' of the instance.. I giess i have to remove that from my brain somehow heheh Thanks Matthew!
01-14-2014 4:21 AM
Yes. This is specially true.
This is quite troubling because it makes it way too easy to access protected variables through 'psuedo' inheritance.
I like it that ABAP requires the subclass to call it's superclass' constructor. This gives you a sense of 'responsibility' in creating subclasses specially if you are simply just accessing a wanted attribute. However, because of this, you dont even have to instantiate anything - Just create a static method. So you can do whatever in the constructor.
Ex. below.. I want to get cl_gui_alv_grid from an salv object. r_controller is a protected variable and cl_salv_model_list is the superclass of cl_salv_table.
CLASS lcl_hack_salv DEFINITION INHERITING FROM cl_salv_model_list.
PUBLIC SECTION.
CLASS-METHODS:
GetGrid
IMPORTING
p_o_model TYPE REF TO cl_salv_model
RETURNING
VALUE(r_o_grid) TYPE REF TO cl_gui_alv_grid.
ENDCLASS.
CLASS lcl_hack_salv IMPLEMENTATION.
METHOD GetGrid.
CHECK p_o_model->r_controller->r_adapter IS BOUND.
CALL METHOD p_o_model->r_controller->r_adapter->('GET_GRID')
RECEIVING value = r_o_grid.
ENDMETHOD.
ENDCLASS.
*** call
DATA: o_alv TYPE REF TO cl_salv_table,
o_grid TYPE REF TO cl_gui_alv_grid,
l_o_grid ?= lcl_hack_salv=>getgrid( r_o_alv ). " Static Call! No instantiation required!!!!!