Section 3.4. Encapsulation, Inheritance, and Polymorphism


3.4. Encapsulation, Inheritance, and Polymorphism

The classic troika of OOP buzzwords is "encapsulation, inheritance, and polymorphism." How does Java do each of these things?

3.4.1. Encapsulation

Encapsulation is the grouping of data and algorithms together into units, and it's also about hiding implementation details that are not important to the users of the unit. The basic unit of encapsulation is the class. All Java code exists in classes. A class is declared with the class keyword (Example 3.24).

Example 3.24. A sample Java class declaration that doesn't actually do anything useful
 public class Sample {   private int id;   public void method()   {     System.out.println(id);   } } 

3.4.2. Inheritance

Inheritance is how a class places itself in a hierarchy of existing classes. In Java, each class inherits from exactly one existing class. A class names the class from which it inherits with the extends keyword. We said a Java class inherits from exactly one class, and yet our Example 3.24 doesn't contain the extends keyword. What gives?

If a class doesn't explicitly extend an existing class, it implicitly extends the "root" Java class, Object. The Object class has some interesting features, and the fact that all Java classes directly or indirectly extend Object has interesting consequences that we will explore later.

Persons coming to Java from another object-oriented language whose name shall remain C++ might wonder about multiple inheritance. Java has the concept of interfaces. An interface is like a class, except that it may not contain data[10] and may contain only method definitions, without any implementation (Example 3.25). An interface may not be instantiated (created with the new operator),[11] so how do you make use of interfaces? Well, a class extends exactly one existing base class, but it may implement any number of interfaces by using the implements keyword (Example 3.26).

[10] Actually, an interface may contain final static data, but since we haven't introduced these concepts yet, just pretend interfaces cannot contain data for now and we'll put the lie to it later.

[11] Although you can do something that looks suspiciously like it with anonymous inner classesbut since we haven't introduced these concepts yet, just pretend that you cannot instantiate an interface; you will see such use later.

Example 3.25. An interface
 public interface Identifiable {   public int getID(); } 

As you can see, a class that implements an interface must provide an implementation of all the methods defined in the interface. We said that an interface cannot be instantiated, but you can declare a variable of type Identifiable and assign an instance of the Sample class to it. In fact, you could assign an instance of any class that implements the Identifiable interface to it.

Example 3.26. A class that implements an interface
 class Sample   implements Identifiable {   private int id;   public void method()   {     System.out.println(id);   }   public int getID()   {     return id;   } } 

Interfaces may also have an extends keyword. In other words, an interface may inherit from an existing interface. This may be useful if you know you will want to use methods from both the extended and the base interface without having to cast the object reference. Otherwise extending an interface is unnecessary since a given class may implement as many interfaces as desired.

3.4.2.1 Inheritance and Encapsulation

Encapsulation and inheritance are related to one another and are controlled by access modifiers on classes, data members, and methods. Let's spend a little time talking about these modifiers and what they mean.

The access modifier keywords are public, private, and protected. When a data member or method is private, it can only be accessed or called from within this specific class. Neither classes that extend this class, nor classes outside this class may access such a data member or call such a method. However, one instance of a class can access private members of another instance of the same class. We don't encourage such use.

When a data member or method is marked protected, however, the only classes that can access such a data member or method are either 1) classes that extend this class and their descendants, or 2) other classes in this package (even if they don't extend this class). Classes in other packages (unless they extend this class) can not get at such members.

A public data member or method may be accessed by any code in any class.

What if you do not put an access specifier on? Then the item (data member, method, or class) has package visibility. Such an item is accessible to any other class within the package, but no further. Not even derived classes, unless they are in the same package, are allowed to see it.[12]

[12] If you are a C++ programmer, the following description may mean something to you (if not, skip this): All classes within a package are essentially "friends."

In terms of how restrictive the access is, you can think of the terms in order of decreasing strictness as:

  • private

  • (package)[13]

    [13] Remember there is no keyword for package level protection, rather it is the absence of a keyword that denotes this level of protection. We had to write something in that space on the page so you'd know what we're talking about.

  • protected

  • public

Tip

Beginner Java programmers often declare everything as public, so that they can ignore such issues. But then they get the OO religion, and having experienced reliability issues (others messing with their variables) they go to the other extreme and declare private as much as possible. The problem here is that they often don't know how others will want to reuse their code. Restricting everything to private makes reuse more narrow. We prefer using private for data members but protected for those internal helper methods that you might otherwise make private; this hides your implementation from most other classes while allowing someone to override your methods, effectively providing a way for them to override your implementation. Allow those who would build on your work the ability to do so without having to reimplement.


Here is a simple example of each type of declaration:

 private String hidn; String pkgstr; protected String protstr; public String wideOpen; 

3.4.2.2 The static statement

Another keyword qualifier on declarations that we need to describe is the static keyword. When a variable is declared static then there is only one instance of that variable shared among all instances of the class. Since the variable exists apart from a particular instance of the class, one refers to it with the class name followed by a dot followed by the variable name, as in System.out.

Similarly, methods can be declared static as well. This also means that you don't need an instance of the class to call them, just the class name, as in System.getProperties().

Now with Java 5.0, you don't even need the class name, provided that you have a static import statement at the top of your class, for example:

 import static java.lang.System.*; 

3.4.2.3 The final statement

Another way that static is often seen is in conjunction with the final keyword. When a variable is declared final then a value can be assigned to it once, but never changed. This can make for good constants.

Since public will make the variable visible to all other classes, static will make it a class variable (available without an instance of that class), and final will keep it from being altered (even though it is publicly available), then combining all of those gives us a good declaration for a constant, for example:

 public static void long lightSpeed = 186000;    // mps 

New to Java 5.0 is the explicit creation of enumerated types. Prior to 5.0, programmers would often use static final constants even when the particular value was unimportant, as a way to provide compile-time checking of the use of the constant values. Here is an example of a declaration of a set of enumerated values:

 enum WallMods { DOOR, WINDOW, VENT, GLASSBLOCK }; 

Tip

A common technique used with public static final constants is to put them in an interface definition. (This is the exception to the rule that interfaces define method signatures but contain no data.) When a class wants to use one or more of those constants, it is declared to implement that interface:

 public MyClass   extends BigClass   implements Comparable, LotsaConstants { ... } 

In defining MyClass we have declared that it implements LotsaConstants (not a name that we recommend you using). That makes all the constants that we have defined inside the LotsaConstants interface available to the MyClass class. Since classes can implement many different interfaces, this doesn't interfere with the use of other "real" interfaces, such as Comparable.


Warning

The keyword enum is new to Java 5.0, so older programs that may have used enum as a variable name and will now cause an error when recompiled for Java 5.0.


The enum will look very familiar to C/C++ programmers, but there are some important differences. In C/C++ the values of the enum elements are, in reality, integer values. Not so in Java. Here they are their own type, but can be converted to a String via the toString() method, with a value that matches the name, for easy reading and debugging.

Enumerated values can be used in == comparisons since they will be defined only once (like other static final constants) and it would only be references that are passed around. They would be referenced by the name of the enumeration followed by dot followed by the particular value (e.g., WallMods.WINDOW) and used as an object. (We have used uppercase for the names not out of any syntax requirement, but only to follow the typical naming convention for constants.)

3.4.3. Polymorphism

Polymorphism (from the Greek poly meaning "many" and morph meaning "shape") refers to the language's ability to deal with objects of many different "shapes," that is, classes, as if they were all the same. We have already seen that Java does this via the extends and implements keywords. You can define an interface and then define two classes that both implement this interface.

Remember our Sample class (Example 3.26). We'll now define another class, Employee, which also implements the Identifiable interface (Example 3.27).

Example 3.27. The Employee class
 class Employee   extends Person   implements Identifiable {   private int empl_id;   public int getID()   {     return empl_id;   } } 

Notice that the same method, getID(), is implemented in the Employee class, but that the field from which it gets the ID value is a different field. That's implementation-specificthe interface defines only the methods that can be called but not their internal implementation. The Employee class not only implements Identifiable, but it also extends the Person class, so we better show you what our example Person class looks like (Example 3.28).

To make a really useful Person class would take a lot more code than we need for our example. The important part for our example is only that it is quite different from the Sample class we saw earlier.

Example 3.29 demonstrates the use of polymorphism. We only show some small relevant snippets of code; there would be a lot more code for this to become an entire, complete example. Don't be distracted by the constructors; we made up some new ones just for this example, that aren't in the class definitions above. Can you see where the polymorphism is at work?

Example 3.28. The Person class
 class Person {   String name;   Address addr;   public   Person(String name, Address addr)   {     this.name = name;     this.addr = addr;   } // constructor   // ... lots more code is here   public String getName()   {     return name;   } } 

Example 3.29. An example use of polymorphism
 //... Sample labwork = new Sample(petridish); Employee tech = new Employee(newguy, 27); Identifiable stuff; //... if (mode) {     stuff = labwork; } else {     stuff = tech; } id = stuff.getID(); 

The key point here is when the call is made to getID(). The compiler can't know at compile time which object will be referred to by stuff, so it doesn't know whose getID() method will be called. But don't worryit works this all out at runtime. That's polymorphismJava can deal with these different objects while you, the programmer, can describe them in a generalized way.

One other related keyword should be mentioned here, abstract. When one declares a class as an abstract class, then the class itself is an incomplete definition. With an abstract class you define all the data of the class but need only write method declarations, not necessarily all the code for the methods. This makes abstract classes similar to interfaces, but in an abstract class, some of the methods can be fully written out.

If you'd like to know more about polymorphism, "late binding," and more of this aspect of Java, read Chapter 7 of Eckel's Thinking in Java. There is an extensive example there with much more detail than we can cover here.



    Java Application Development with Linux
    Java Application Development on Linux
    ISBN: 013143697X
    EAN: 2147483647
    Year: 2004
    Pages: 292

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net