Thursday, March 20, 2014

Fun With Java Access Modifiers

I recently came across a Java class that had a protected list of private inner class objects. That is
public class Parent {
   private class Thing {
      // ...
   }
   
   protected List <Thing> things = new LinkedList<Thing>();

   // ...
}
    
Obviously this is a code smell because if I were to extend the Parent class, even though I can access the list of things I can't actually access any of their fields or methods.

public class Child extends Parent {
   
   public Child() {
      super();

      for(Thing thing : things) { // <- Compile Error: Parent.Thing not visible
        // ...
      }
   }
}
Your first response at remedying this issue might be to change the access modifier for the list from protected to private.
private List<Thing> things = new LinkedList<Thing>();
Unfortunately you'd still have to worry about future developers accidentally returning the list in a method. For instance maybe they execute the "Generate Getters and Setters" task in Eclipse.

What about extracting the Thing class into its own file? Would that be any better? Perhaps, but only if the private inner class doesn't depend on data or methods of the outer Parent class.

Probably the best solution is to have the private inner class implement an interface that exposes public getter methods for the fields you want to access. Then you'd have a list of objects that implement that interface.
public class Parent {
   private class Thing implements Contract {
      // ...
   }

   protected List<Contract> things = new LinkedList<Contract>();

   // ...
}
Note: This is just following the old programming adage that you should program to an interface and not an implementation.

As I was fooling around with the different access modifiers in the private Thing class I learned something kind of interesting. The outer Parent class can access private members of the private inner Thing class.
public class Parent {
   private class Thing {
      private String str = "Hello World";
   }
   
   public Parent() {      
      Thing thing = new Thing();
      System.out.println("str = " + thing.str);
   }   
}
I was already aware that I could access private members of the outer class in the inner class.

"A nested class is a member of its enclosing class. Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private." (The Java Tutorials' Nested Classes)
public class Parent {
   private String str = "Hello World";
   
   private class Thing {
      public Thing() {
         System.out.println("str = " + str);
      }
   }   
}
    
What I didn't realize, until just recently, is that it worked the other way as well. "The inner class," Thilo writes on stackoverflow, " is (for purposes of access control) considered to be part of the containing class." Further down the forum another user named TofuBeer writes: "There is no way for a method declared in a class to not be able to access all of [its'] members.  It stands to reason that the inner classes has unfettered access to the members of the outer class and the outer class has unfettered access to the members of the inner class."

No comments:

Post a Comment