Donnerstag, 23. September 2010

Stealing in Java or an OO misconception

In July my coworker wanted to make a list printable on a summary screen in a ticketing application from the vendor WasGreatIn90s.

The application is written in Java. The list is represented by a class that lives in the vendor’s application code entirely. He wanted to add a custom print button on the summary screen. He registered a custom callback for executing the printing and noticed that he could get a reference to the list object on the screen.

ListFromVendor vendorList = GUIAPI.obtainVendorListFromGUIAPI();

The ListFromVendor class was basically something like this:

class ListFromVendor {
  protected ArrayList list;
  // functions simplied to add/manipulate list 
  ...
  public void update(String item) {
    list.add(item); 
  }
  ...
  // but it cannot print
  // and no public get method for list
}
The problem was that the list class could not print itself. But my coworker had already a class that was able to print a list:

class ListThatPrints {  
  ArrayList list;
  ListThatPrints(ArrayList list) {
    this.list = list;
  }
  public void print() {  
    // just some code to give an idea   
    for(int i=0;i<list.size();i++) {
      System.out.println(""+i+"/"+list.get(i));
    }
  }

The list data in ListFromVendor is protected and there is no get method to extract the list data directly.

He thought: "Subclassing the ListFromVendor in MyListFromVendor would get me access to the list data itself and the printing could be added by transforming the list data into an instance of the ListThatPrints and just using the print capability of ListThatPrints."

class MyListFromVendor extends ListFromVendor {
  ...
  public void print() {
    ListThatPrints printableList = 
      new ListThatPrints(list);
    printableList.print();
  }
  ...
}

If so he would also need to alter the vendor’s factory code somewhere to substitute the screen’s list object class to MyListFromVendor, but that was a no-go in the company (to modify the vendor’s code).

What to do now? He thought he needs to reimplement the whole summary screen with a ListThatPrints class in a custom summary screen which would have been ok with company policies. The estimation for that would be at least 2 days since the GUI framework used is quite horrible.

He told me the story and I thought about it.

After a while I had the ListThatSteals:

class ListThatSteals extends ListFromVendor {
  public static ArrayList stealData(ListFromVendor nonPrintingList) {
    return nonPrintingList.list;
  }  
  public static void main(String[] argv) {
    ListFromVendor nonPrintingList =GUIAPI. obtainVendorListFromGUI();
    ListThatPrints printingList = 
      new ListThatPrints(
              ListThatSteals.stealData(nonPrintingList));
    printingList.print(); 
  }
} 

He was stunned.

The misconception was I guess that you can only access from an instance of a subclass the direct parent instance of that subclass.
But as shown in the ListThatSteals you can access the protected variables of any parent instance of a class from any of its subclass instances.

2 Kommentare:

  1. Doesn't work in C#, just tried it. Compiler error msg is something like:

    Cannot access protected member 'Foo._myField' via a qualifier of type 'Foo';
    the qualifier must be of type 'FooSubclass' (or derived from it)


    And I think it shouldn't work, since it violates the principle of encapsulation. private/protected member fields are not supposed to be accessible, not even for objects which happen to be of the same type (or a subtype).

    So not sure why Java allows doing this.

    AntwortenLöschen
  2. For the same type it works since you need to be able to implement a copy constructor/method.

    AntwortenLöschen