Skip to main content

Controlling access with implementation hiding in Java

Posted by hellofadude on December 8, 2013 at 6:28 PM PST

A key consideration for the library designer in the normal conduct of operations is maintaining the ability to make changes or improvements to the library at any time without requiring the consumers (client programmers) of that library to do the same. In Java, a library consists of a logical grouping of .class files packaged together to make a working program.

An apt analogy to this point may be to imagine the considerable inconvenience you might suffer, if, as a result of an update to the Java SE library, you were compelled to make wholesale changes to your existing classes that worked well with a previous version.
As a consumer, you rely on the parts of a library on which your code depends staying the same, and a situation where this ceased to be the case would be very problematic to say the least.
As a matter of custom, an 'unwritten' agreement exists whereby the library designer agrees not to alter or remove existing methods on which your code depends, when modifying some part of a library, since that would break the client programmers code, and make a mockery of years of trusted convention. To do this, the library designer must figure out a way to separate the parts that change in his code from the parts that do not. More precisely, the designer must look to the concept of implementation hiding.

Implementation hiding in Java is closely aligned to the need for access control by providing a way to allow the library designer to say what is available to the client programmer (i.e. the parts that do not change) and what is not (i.e. the parts that do change) by putting boundaries within a data type that help separate the interface from the implementation.

This interface/implementation distinction is an important point to understand as even though an interface is, by convention, accessible to the client programmer, the implementation which is a part of the structure of the library is hidden or not accessible to the user. In this way, the designer can make changes to the internal mechanism, which we now understand to be quite apart from the interface, without affecting or requiring the client programmer to make any changes in the way he(or she) uses such library.

Java provides access specifiers that enable the library creator to say what is available to the client programmer and what is not by controlling access to data types and the members of which they are comprised. These access specifiers consist of the keywords public, protected and private, each reflecting a different level of access from 'most access' to 'least access'. There is also the default package access which occurs when a programmer fails to specify any of the above access specifiers. In subsequent sections, I discuss these specifiers in more detail, however we would do better to begin with a discussion as to how components in Java are bundled together into a coherent library unit.

package: Library unit

In Java, a package consists of a grouping of classes organised under a single namespace. We may perceive a package as a mechanism by which one may mitigate the risk that those classes one writes for the web do not collide with that of some other programmer simply because they may have the same class name. The logic for this arrangement may be linked to the uniform interface constraint, a key constraint within REST that applies the principle of generality to component interfaces.

In Java, a class file has a .class extension and is normally a product of your source code file. You see, when you write Java programs, you create source code files that comprise a compilation unit, usually with a .java extension. Each compilation unit must have only one public class with the same name as the file (without the .java extension) within which it exists. The Java compiler is responsible for producing output files with the .class extension, from a group of these source code files with the .java extension. These output files are what comprise the library.

The package keyword in Java is the mechanism by which you may package all your source files into a single namespace and ultimately reduce the risk of collisions. A package in Java would normally contain many .class files and the package keyword is one way Java is able to reference them. Understanding the semantics behind this mechanism is important if you intend to program effectively in Java.

The package keyword must appear as the first non-comment line in your Java class file; and following the keyword should be the name of a directory on your system within which your files must reside and which must be searchable beginning from the CLASSPATH. By so doing, Java takes advantage of the hierarchical file structure of your operating system by allowing you to implicitly specify a directory structure when you assign a package, a name. For instance, suppose I wanted to create a package for my source code file MySourceCode.java in the kingsley.java directory, I would do something like this:-

  package kingsley.java;

  public class MySourceCode { ... }

The above example says the file MySourceCode.java is located within the package kingsley.java. The Java compiler is responsible for producing the associated MySourceCode.class file, normally in a separate directory from the source file.

Certain semantics allow for the concatenation of a string that describe the location of .class file directories relative to the root by which the Java interpreter may be able to find and load these files.

The import keyword allows you to specify or import a package to use within your own program. For instance, if you wanted to use my MySourceCode.java file in your own program, you would do something like this:-

  package user.java;

  import kingsley.java.MySourceCode;
  import java.util.*;
 
  public class MyUserFile { ... }

With the import statement, the MySourceCode.java file becomes available to your program. If there were, for instance more than one file in the kingsley.java package you were interested to use, instead of importing each file individually, you would use the wildcard character (*). Take the second import statement for example which is used to import all the files java.util package.

It is still possible in some rare instances where you may import two libraries that contain the same class name making a collision inevitable when you try to create a reference to an object. In these circumstances you will be required to specify the full class name including the package within which it resides. For instance, lets us suppose we have our own Random class in the kingsley.java package and then we also import the java.util library which also contains a Random class like so:-

  import kingsley.java.*;
  import java.util.*;

A collision would definitely occur if we tried to make a Random object reference like so:-

Random rand = new Random();//! Error, Which Random

You would most likely receive a compiler error forcing you to be explicit. You can avoid this by specifying the full class name like this:-

  java.util.Random rand = new java.util.Random();

This gives the compiler the exact location of your intended Random class and ensures you avoid compiler errors.

Access Specifiers

Access specifiers in Java include the keywords public, private and protected which are placed on the same line and immediately preceding each definition of the members of your class. Each specifier provides controlled access only to that particular member definition.

If you do not specify any access control for a particular class, it automatically defaults to what we call package access.

Package access

Package access is the default access when you fail to specify any access control option, which means that class will become accessible only to those classes within the same package but will appear as private to other classes outside of the package.

We can think of package access as a way to group related files together granting mutual access and interaction to classes within the same package while restricting access to others. There are four ways in Java to grant access to members within a particular class:-

i. You might include the public keyword to make a class member accessible to everyone, everywhere.

ii. Not specify any access control thereby defaulting to package access.

iii. Include the protected keyword which allows an inherited class to access the member.

iv. Provide accessor/mutator methods (get/set) that read and change members' value.

public: interface access

The public keyword allows you to grant access to any member of your class to everyone, particularly those client programmers who rely on these portions of your code staying the same; examine the following class:-

  package kingsley.java;

  public class Switch {
    public Switch() {
       System.out.println("Switch constructor");
    }
    public void on() {
       System.out.println("Switch on");
    }
    void off() {
       System.out.println("Switch off");
    }
  }

Now suppose you created another class outside of this kingsley.java package, you would be required to import the Switch.java class in order to access any of its members like so:-
  import kingsley.java.Switch;

  public class Light {
    public static void main(String[] args) {
       Switch sw = new Switch();
       sw.on();
       sw.off(); //Error, can't access
    }
  }
  /* Output
  Switch constructor
  Switch on
  *//

You might glean from the output that the Light class is able to access the constructor member of the Switch class, as well as the on() method. This is because both methods have been declared public. The off() method on the other hand, is inaccessible to the Light class because it has no access specifier, it defaults to package access. Note that when you declare a member public, anyone can access it.

Tip: To run the example, comment out the off() method

private: hidden access

When you specify the private keyword in relation to a member of a class, you make that member inaccessible to anyone else, and the consumers of your library are restricted from using it. This is in effect a way of hiding implementation of the 'parts that change' in your program to give you, as the library creator, the ability to modify it without impacting on your consumers code. Further, private members of a class are only available to methods within the same class. Consider the following example:-

  package kingsley.java;
  
  class Egg {
    private Egg() {
       System.out.println("Egg constructor");
    }
    static Egg makeEgg() {
      System.out.println("in makeEgg()");
      return new Egg();
    }
  }

  public class Breakfast () {
    public static void main(String[] args) {
       Egg egg1 = new Egg(); //! Error can not Make new Eggs
       Egg egg2 = Egg.makeEgg();
    }
  }
  /* Output
  in makeEgg()
  Egg constructor
  *//

In this example, both classes are actually in the same package and it demonstrates how to use the private keyword to control how an object is created by preventing a user from directly accessing the class constructor. In this instance, the user is prevented from directly creating an Egg object by applying the private keyword to the Egg constructor. In the main() of the Breakfast class, any attempt to create an Egg object directly is certain to induce a compiler error, however in this instance, we are forced to use the static makeEgg() method, a member of the same class to create an Egg object. In this way, a client programmer may use the makeEgg() method to construct a new Egg object even as the class designer is free to change the way this object is constructed without having to worry about affecting the consumers code.

protected: inherited access

The protected access control specifier is relevant to the concept of inheritance in Java. Inheritance refers to the mechanism by which a derived class may inherit the members of it's base class. It is a fundamental concept in OOP that says take this existing class (base class) and add new members to a new class without interfering with the existing class in any way. Derived classes are subclasses of a base class and provide a simple way to extend the functionality of data types in Java.
To cause your class to inherit the members of another class, you use the extends keyword like so:-

class Light extends Switch { ... }

The protected keyword allows a class designer to grant access to derived classes but not to everyone in general. It is a kind of midway point between the private and public access control specifiers.
In the previous example, the Light class was unable to access the off() member in the Switch class which defaulted to package access. We could modify our Light class by inheriting from the Switch class and adding a constructor and a couple of public methods to elucidate further;-
  package kingsley.java;

  public class Switch {
    public Switch() {
       System.out.println("Switch constructor");
    }
    public void on() {
       System.out.println("Switch on");
    }
    void off() {
       System.out.println("Switch off");
    }
  }

  package kingsley.java.com;

  import kingsley.java.*;

  public class Light extends Switch {
     public Light() {
        System.out.println( "Light constructor" );
     }
     public void lightOn() {
        on();
     }
     public void lightOff() {
        off();//!Error can not access
     }
     public static void main(String[] args) {
        Light ls = new Light();
        ls.lightOn();
        ls.lightOff();        
     }
   }
   /*Output
   Switch constructor
   Light constructor
   Switch on
   **//

The interesting thing to note in this example is you would expect the Light class being a derived class of Switch class to inherit all its members including the off() method. But this is not so in this case because the Light class exists in a different package to the Switch class and the lack of an access control specifier ensures the off() member remains inaccessible. To get around this, we could add the protected keyword to our off() member in the following manner:-

package kingsley.java;

  public class Switch {
    public Switch() {
       System.out.println("Switch constructor");
    }
    public void on() {
       System.out.println("Switch on");
    }
    protected void off() {
       System.out.println("Switch off");
    }
  }

  package kingsley.java.com;

  import kingsley.java.*;

  public class Light extends Switch {
     public Light() {
        System.out.println( "Light constructor" );
     }
     public void lightOn() {
        on();
     }
     public void lightOff() {
        off(); //protected method
     }
     public static void main(String[] args) {
        Light ls = new Light();
        ls.lightOn();
        ls.lightOff();
     }
   }
   /*Output
   Switch constructor
   Light constructor
   Switch on
   Switch off
   **//

The protected keyword makes the off() member accessible to any class inheriting from the Switch class, even to those that exist in a different package.

class access

It is also possible to use access specifiers to determine which classes are available within a library to a user of that library. To make classes available to a consumer of a library, just specify the public keyword on the entire class definition like so:-

public class Shape { ... }

However, if on the other hand you were inclined to deter the client programmer from becoming reliant on that class, so that you could re-engineer it at some later time, you would make it inaccessible or hidden from the consumer by leaving out the public keyword in which case the class defaults to package access, making it like so:-

class Shape { ... }

You should be aware that in this scenario, it will make sense to make fields of the class private - which is always good practice, even for your classes that are public. It is also considered within reason to give methods the same access as your class, which in this case is package access. Note that it is not possible for a class to be private or protected. If you must restrict anyone from having access to your class, the best way would be to make all constructors private, thereby preventing anyone but yourself from making access from within a static member of the same class.

Summary

In this discussion, we have identified the convention that exists between the library designer and the client programmer. This 'unwritten' agreement provides rules that guide custom to ensure a certain amount of universality in the operation of software.

We have also briefly discussed some of the mechanisms and components for access control that exist within the Java programming language at reasonably higher level of abstraction to simplify understanding.

There are two primary motivations for controlling access to class members. The first motivation is to be able to separate the publicly accessible interfaces from the internal workings of the program. The public interfaces are only relevant to the client programmer and by restricting some members, you are actually making it easier for consumers to use your library.

The second reason is to allow the library designer the flexibility to change the internal workings of the program without having to worry about affecting the consumers code.

contact me @ kaseosime@btinternet.com