Chapter 9: Encapsulation, Data Hiding & Inheritance 9.0 Introduction Encapsulation literally means enclosing many smaller things within a larger outer cover. Under the cover of a class definition, many data and method members remain covered. So a class actually encapsulates a good number of variables and methods. The internal details of a class can be kept hidden from a user by a technique called information or data hiding. Such information or data hiding is necessary so that user cannot do any damage or harm to the sensitive data or methods. Moreover, data hiding principle should allow users to use classes without knowing their inner details. To achieve such a goal, java provides visibility modifiers or access specifier like private, public, default (i.e. when not specifier) or protected, etc. The protected specifier can be applied only to inherited subclasses. A subclass can be derived from a base class (called superclass) by inheriting the latter’s data and method definitions over which any further addition of data or function members can be made to fulfil specialized requirements of the subclass. This chapter deals with the principles and practice of encapsulation, data hiding and inheritance. 9.1
Use of private & public
When a member of a class (data or method) is specified as public, any other class or external codes can get access to that member. The main () method is always made public so that run-time classes or codes can have direct access to it. When a member of a class is specified as private, only other members of its own class and none else can access that member. By default, that is when no access specifier is explicitly mentioned, the member of a class will be treated as public within its own package, but cannot be accessed by any member outside of that package. To protect your data from unwanted access, you can allow access only through member methods and specify those members as private or public according to application requirements. An access specifier is mentioned first in a member’s type specification. For example -public int x; private int method( double a, char b) { ..... } etc.
Example-9.1 will show how private data variable can be accessed using get () and set () methods only. /* Example-9.1 Demo of Access Control */ class AccessVariables { int x; // default, so direct access possible public int y; // public, so direct access possible private int z; // private, // so needs methods to access it // for which methods are defined now void setz (int j) { z = j; } int getz () { return z; } } public class TestAccess { public static void main() { AccessVariables avs = new AccessVariables(); avs.x = 12; avs.y = 65;
//x value can be directly set //y value can be directly set
avs.setz(98); // value set through setz() method System.out.println (" x is = " + avs.x); System.out.println (" y is = " + avs.y); System.out.println (" z is = " + avs.getz()); } }
//direct access //direct access //access through a method
If you run this program, you will see the output – x is = 12 y is = 65 z is = 98 You have noticed that to access the private variable z, the methods getz() and setz() have been used by the class TestAccess as the variables (x, y, z) were defined in a separate class AccessVariables. Thus proper access specifier can protect sensitive data and methods.
9.2
Scope & Visibility Rules
The scope rules are the rules by which it is decided how a particular data item or method can be accessed in a program environment. Visibility is also a related term used with respect to access control. Visibility is concerned with the question whether a specified variable can be used from a given place in a program. 9.2.1
Scope & Visibility Rules of Java
* Data declared as instance variables in a class, called global data, can be used by all the methods belonging to that class. • Data declared within a method, called local data, can be used only in that method. • If a local variable having the same name as that of a global variable, then local variable will hide the global variable. • Variables declared in a block are visible to every method in that block. • Variables declared in an inner block are not visible outside that block. • Variables declared in exterior blocks are always visible to interior blocks. • A class has only two levels of access -- public or default. • A subclass (i.e. inherited class) cannot access the private members of the superclass (i.e. base class). The scope and visibility rules can be summarized in a tabular form as shown in Table-9.1. Scope Visibility ------------------------------------------------------------------------------------------------------------Public variables, methods & Classes public visible to all classes Default variables, methods & Classes
Private variables, methods
treated as public within its package private
visible to all classes within the package visible only within The class
& Classes Protected variables, methods & Classes
protected
Table-9.1 Scope & Visibility Rules Table
visible to all classes within the package & inherited classes to outside package
9.3
Packages & Package level Access
Packages add another level of access control. Classes act as containers of data and methods, whereas packages act as containers of classes, interfaces and sub-packages. Java’s smallest unit of abstraction is class. Class members can be of four categories:-• •
• •
Subclasses (inherited from superclass) within the same package. Non-subclasses (i.e. different classes) existing in the same package. Subclasses from different packages. Classes that are neither in the same package nor existing as subclasses.
Anything declared public can be accessed from anywhere. Anything declared private couldn’t be seen outside that class. Absence of specifier, i.e. default, ensures visibility to classes and subclasses within the same package. Protected specifier can be used when you want to allow an element to be seen outside your current package, but only for the classes inherited from the base class or superclass. The package level access rules can be displayed in the form of a table as shown in Table-9.2.
Private
Default
Protected
Same Package subclass
No
Yes
Same Package non-subclass
No
Yes
Yes
Yes
Different Package subclass
No
No
Yes
Yes
Different Package non-subclass
No
No
Yes
No
Yes
Public Yes
Table – 9.2 Package Level Class Member Access 9.4
Importing Packages
Normally Java classes remain stored in some named packages. Java includes the import statement to bring any particular class/es or entire package into visibility of a particular program. Once imported, a class can be referred directly. Import statement(s) should occur immediately following the package statement, if any, and before any class defined by users. The general form of the import statement looks like: import pkg1[.pkg2].class_name | * ( indicating all);
where pkg1 is the top level package, pkg2 is the subordinate package inside the outer package separated by a dot (.). Java’s all standard library classes remain stored in a package called java. All library classes essentially required to execute any java program remain stored in a sub-package called java.lang, which is automatically imported by java compiler with any java program while compiling. When a package is imported, only those items specified as public in that package will be made available for users’ programs. Let us now examine one example program which makes use the java’s IO library package along with one user defined class Wrap1Demo (example-9.2) that is capable of displaying a given decimal number with its equivalent binary, octal and hexadecimal representations. Example- 9.2(a) User Defined Wrap1Demo class public class Wrap1Demo { int number; public void display (int number){ System.out.println (" binary equivalent of " + number + " is = " + Integer.toBinaryString (number)); System.out.println (" octal equivalent of "+ number + " is = " + Integer.toOctalString (number)); System.out.println (" hexadecimal equivalent of " + number + " is = " + Integer.toHexString (number)); } } Example-9.2 (b) ConvertDemo class imports both java’s Library IO Package and the Wrap1Demo (stored in the BlueJ’s Default Package area) import java.io.*; import Wrap1Demo;
//one library package //one user defined class
public class ConvertDemo { public static void main() throws IOException { BufferedReader invalue = new BufferedReader (new InputStreamReader (System.in)); String str;
int number; System.out.println (" Enter a decimal integer value."); str = invalue.readLine (); try { number = Integer.parseInt (str); System.out.println ("Number you have entered is = "+ number); Wrap1Demo value = new Wrap1Demo() value.display(number); } catch (NumberFormatException e) { System.out.println (" Invalid Entry."); } } } Compile and run this program to see how by entering a decimal integer value, you can have its equivalent representation (Picture 9.1) in binary, octal and hexadecimal.
Picture 9.1 (Verify the results by hand calculations) 9.5
Inheritance
Inheritance is one of the most important characteristics of Object Oriented Programming. From a common base class ( or superclass) many specialized sub-classes called derived classes can be created for use in a java program. For example, from a common base class Employee --- many special sub-classes like TechEmployee, AdminEmployee, OfficeEmployee, etc can be created by inheriting the common data and method members of the base class Employee and by adding other suitable member definitions (both data and method) specially required for specialized sub-classes. Therefore, a sub-class can be regarded as a specialized or extended version of a superclass or base class.
9.5.1
Inheritance Basics
To inherit the properties of a base class we have to make use of the keyword extends. We will now examine how from a superclass (i.e. base class) we can derive a sub-class and can use both together for problem solving. Study example-9.3 carefully. Example – 9.3 Demo of a sub-class inheriting properties of a superclass /* Write a description of class SuperA first */ public class SuperA { int i,j; void show () { System.out.println (" values of i and j :" + i + " } }
" + j);
// create a sub-class SubB from a SuperA public class SubB extends SuperA // using inheritance { int k; void showk() { System.out.println ("value of k:" + k); } void sum() { System.out.println ("sum of i, j, k =" + (i + j + k)); } }
//use of both Base & Derived classes in a class with main() method public class Inheritance { public static void main() { SuperA supobj = new SuperA(); SubB subobj = new SubB();
supobj.i = 20; supobj.j = 30; System.out.println (" i & j of super class:"); supobj.show(); System.out.println ("*************************************"); subobj.i = 15; subobj.j = 25; subobj.k = 40; System.out.println (" sub-object data values:"); subobj.show(); subobj.showk(); System.out.println ("Sum of sub-object's values:" ); subobj.sum(); } }
If you run this program, the following output will appear on the terminal window --i & j of super class: values of i and j :20 30 *************************************
sub-object data values : values of i and j :15 25 value of k:40 Sum of sub-object's values: sum of i,j,k = 80 Try to understand the usefulness of the inheritance property of Object Oriented Programming. Now we will examine how multi-level inheritance can be used in java programming (Example-9.4) Example-9.4 Demo of Multi-Level Inheritance /** Definition of a Base Class */ public class Student
{
String name; String branch; String department; private String hno_road; private String town; private int pincode; /** * Constructor for objects of class Student */ public Student(String fullname, String discipline, String dept) { name = fullname; branch = discipline; department = dept; } /** * An example of a method */ public void setAddress (String road, String city, int pin) { hno_road = road; town = city; pincode = pin; } public void displayDetails() { System.out.print (name + " "+ branch + " System.out.println (); System.out.println (hno_road); System.out.println (town); System.out.println (pincode); } } // First level inheritance public class TechStud extends Student { String category; String specialization;
"+ department);
/** * Constructor for objects of class TechStud */ TechStud(String sname, String sdisp, String sdept, String quota ) { super ( sname, sdisp, sdept); category = quota; } /** * An example of a method */ public void setMajor(String major) { specialization = major; } public void getFullDetails(){ System.out.println (category + " }
"+ specialization);
} /** Second level inheritance */ public class PGTechStud extends TechStud { String thesisName; String guide; /** * Constructor for objects of class PGTechStud */ public PGTechStud(String pgsname, String pgsdisp,String pgdept, String special, String sguide) { super ( pgsname, pgsdisp, pgdept, special); guide = sguide; } /** * An example of a method */
public void setThesisName(String thesis) { thesisName = thesis; } public void allDetails() { System.out.println (guide + " "+ specialization); System.out.println ("Thesis on : " + thesisName); } } /* Demo of Multi-Level Inheritance */ public class DemoMLinherit { public static void main(){ // different objects created PGTechStud pgEngg_1 = new PGTechStud("Amit Roy", " Engg", " Mechanical","Heat Power", "Prof A.Pal"); Student stud_1 = new Student(" Amita Adak", "Science", "Physics" ); TechStud ugstud_2 = new TechStud ("Sudip Das", "Engg", "Electronics", "general"); stud_1.setAddress("230 B T Road,", "24 Pgs(s),", 700127); stud_1.displayDetails(); System.out.println (); ugstud_2.setAddress(" 23 C R Avenue,", "Kolkata-", 700001); ugstud_2.displayDetails(); ugstud_2.setMajor("Microwave"); ugstud_2.getFullDetails(); System.out.println (); pgEngg_1.setAddress(" 235 A J C Bose Road,", "Kolkata -", 700017); pgEngg_1.displayDetails(); pgEngg_1.setMajor("Heat Power"); pgEngg_1.setThesisName(" Improvement of Thermal Efficiency"); pgEngg_1.allDetails(); } } . If you run this program, the output will appear on the terminal window as shown in Picture 9.2 - --
Picture 9.2 Study example-9.4 very carefully to see how from a base class Student, TechStud subclass is derived as a first level inheritance and then the TechStud is further extended to PGTechStud as a second level inheritance. The DemoMLinhirit class containing main () method controls the members of all the classes as per application requirements. Please note that library classes present in the java packages can be used as base classes to derive subclasses as per software requirements. Thus software development becomes easier and simpler by reuse of library classes either directly or by deriving classes from them utilizing the inheritance property. To know more about java’s inheritance properties, you have to consult the Complete Reference: Java 2. 9.6 Java’s Interfaces Interface literally means a connection mechanism between two dissimilar entities. In java, it is used as a tag to differentiate between a fully defined class and a skeletal or an abstract definition of a class. The keyword interface allows a class definition in an abstract form without showing any implementation details. Using interface, one can specify what a class is supposed to do, but not telling how it can do that.
Interfaces syntactically look like classes without having any body specified. That means, variables and methods are not defined within an interface keeping scope of implementation details in various ways. Interfaces can be used to support dynamic method binding at run time. Java does not allow Multiple inheritance (i.e. inheriting properties from different base or super classes instead of from a single base or super class as used in C++). An interface can help implementing multiple inheritance like features in java, if required at all. Just remember that java allows only multi-level single inheritance but not multiple inheritance. Interface can also disconnect the definition of a method or a set of methods from the inheritance hierarchy and can allow “ one interface, multiple methods” aspect of polymorphism. An interface is defined just like a class having a general structural form --[public | not used] interface name { method_1 ( parameter-list); < ret-type> method_2 (parameter-list); final-varname1 = value; | | method_M (parameter-list); final-varnameM; } Once an interface is defined, one or more classes can implement that interface by including the implement clause in a class definition. The general form of a class which includes the implement clause looks like ---[public | not used ] class classname [ extends superclass] [ implements interface [, interface ...]] { // class body } Now an example will be show that will implement the stack operations – push & pop – by taking help of an interface. (See Example-9.5) Example-9.5 Stack Implementation using interface /** An integer Stack interface is defined here */ interface IntegerStack {
void push(int data); int pop(); } /** * a description of class FixedLenthStack */ public class FixedLenthStack { // instance variables private int flstack[]; private int topostk; /** * stack initialization */ public FixedLenthStack(int size) { flstack = new int[size]; topostk = -1; } public void push ( int data) {if (topostk = = flstack.length-1) System.out.println (" Stack overflow."); else flstack [++topostk] = data; } public int pop() { if (topostk <0) { System.out.println (" Stack Underflow."); return 0; } else return flstack[topostk--]; } } /** * Write a description of class DemoInterface here. */
public class DemoInterface { public static void main() {FixedLenthStack stk_1 = new FixedLenthStack(6); FixedLenthStack stk_2 = new FixedLenthStack(12); for ( int i=0; i < 6; i++){ int j = 2*i; stk_1.push(j); } for (int i=0; i<12; i++) { int j = i+2; stk_2.push(j); } System.out.println (" stk_1 stored following 6 data."); for (int i =0; i <6; i++) System.out.print (stk_1.pop()+ " "); System.out.println (); System.out.println (" stk_2 stored following 12 data."); for ( int i =0; i <12; i++) System.out.print (stk_2.pop() + " "); System.out.println (); } } If you run this program, the output will appear as shown below: ---stk_1 stored following 6 data. 10 8 6 4 2 0 stk_2 stored following 12 data. 13 12 11 10 9 8 7 6 5
4
3
2
The output reveals the fact that a stack is having a Last in First out (LIFO) data structure.
9.6
Conclusions
Encapsulation & Data hiding are the two important aspects of the Object Oriented Programming paradigm. Instance variables and methods, encapsulated within a class, can be further protected from misuse by using access specifiers like private, public, etc. The topics
have been discussed in details using examples. The scope and visibility rules have been explained and presented in a tabulated form. The package level access control rules are also shown in the form of a table. With appropriate examples, it has been shown how java’s library and predefined user classes can be imported and used together to construct a new class to fulfil various application requirements. The inheritance property of java has been explained with suitable examples. Java supports only single inheritance but not multiple inheritance like C++. However multi-level inheritance is very much possible and allowed by java. The importance of java’s interface has just been touched upon.