Friday, December 7, 2007

Inheritance

There are two fundamental mechanisms for building new classes from existing ones:

inheritance and aggregation. It makes sense to inherit from an existing class Vehicle to define a class Car,

since a car is a vehicle. The class Vehicle has several parts; therefore,

it makes sense to define a composite object of class Vehicle that has constituent objects of such classes as Motor,

Axle, and GearBox, which make up a vehicle.

Inheritance is illustrated by an example that implements a stack of characters that can print its elements on the terminal.

This new stack has all the properties and behaviors of the CharStack class,

but it also has the additional capability of printing its elements.

Given that this printable stack is a stack of characters, it can be derived from the CharStack class.

This relationship is shown in Figure The class PrintableCharStack is called the subclass,

and the class CharStack is called the superclass.

The CharStack class is a generalization for all stacks of characters,

whereas the class PrintableCharStack is a specialization of stacks of characters that can also print their elements.

In Java, deriving a new class from an existing class requires the use of the extends clause in the subclass definition.
A subclass can extend only one superclass. The subclass inherits members of the superclass.
The following code fragment implements the PrintableCharStack class:
class PrintableCharStack extends CharStack {                          // (1)
// Instance method
public void printStackElements() { // (2)
// ... implementation of the method...
}

// The constructor calls the constructor of the superclass explicitly.
public PrintableCharStack(int capacity) { super(capacity); } // (3)
}

The PrintableCharStack class extends the CharStack class at (1).

Implementing the printStackElements() method in the PrintableCharStack class requires access

- to the field stackArray from the superclass CharStack.

However, this field is private and therefore not accessible in the subclass.

The subclass can access these fields if the accessibility of the fields is changed to protected in the CharStack class.

Example 1.3 uses a version of the class CharStack,

which has been modified accordingly. Implementation of the printStackElements() method is shown at (2).

The constructor of the PrintableCharStack class at (3) calls the constructor of the superclass CharStack -

in order to initialize the stack properly.

Example 1.3 Defining a Subclass
// Source Filename: CharStack.java
public class CharStack {
// Instance variables
protected char[] stackArray; // The array that implements the stack.
protected int topOfStack; // The top of the stack.

// The rest of the definition is the same as in Example 1.2.
}

// Source Filename: PrintableCharStack.java
public class PrintableCharStack extends CharStack { // (1)
// Instance method
public void printStackElements() { // (2)
for (int i = 0; i <= topOfStack; i++)

Objects of the PrintableCharStack class will respond just like the objects of the CharStack class,

but they will also have the additional functionality defined in the subclass:

PrintableCharStack aPrintableCharStack = new PrintableCharStack(3);
aPrintableCharStack.push('H');
aPrintableCharStack.push('i');
aPrintableCharStack.push('!');
aPrintableCharStack.printStackElements(); // Prints "Hi!" on the terminal
Basics of Java Programming

Objects

Class Instantiation

The process of creating objects from a class is called instantiation. An object is an instance of a class. The object is constructed using the class as a blueprint and is a concrete instance of the abstraction that the class represents. An object must be created before it can be used in a program. In Java, objects are manipulated through object references (also called reference values or simply references). The process of creating objects usually involves the following steps:

  1. Declaration of a variable to store the object reference.

    This involves declaring a reference variable of the appropriate class to store the reference to the object.

    // Declaration of two reference variables that will denote
    // two distinct objects, namely two stacks of characters, respectively.
    CharStack stack1, stack2;
  2. Creating an object.

    This involves using the new operator in conjunction with a call to a constructor, to create an instance of the class.

    // Create two distinct stacks of chars.
    stack1 = new CharStack(10); // Stack length: 10 chars
    stack2 = new CharStack(5); // Stack length: 5 chars

    The new operator returns a reference to a new instance of the CharStack class. This reference can be assigned to a reference variable of the appropriate class.

    Each object has a unique identity and has its own copy of the fields declared in the class definition. The two stacks, denoted by stack1 and stack2, will have their own stackArray and topOfStack fields.

    The purpose of the constructor call on the right side of the new operator is to initialize the newly created object. In this particular case, for each new CharStack instance created using the new operator, the constructor creates an array of characters. The length of this array is given by the value of the argument to the constructor. The constructor also initializes the topOfStack field.

The declaration and the instantiation can also be combined:

CharStack stack1 = new CharStack(10),
stack2 = new CharStack(5);

Figure shows the UML notation for objects. The graphical representation of an object is very similar to that of a class. Figure shows the canonical notation, where the name of the reference variable denoting the object is prefixed to the class name with a colon ':'. If the name of the reference variable is omitted, as in Figure, this denotes an anonymous object. Since objects in Java do not have names, but are denoted by references, a more elaborate notation is shown in Figure , where objects representing references of CharStack class explicitly refer to CharStack objects. In most cases, the more compact notation will suffice.


Object References

A reference provides a handle to an object that is created and stored in memory. In Java, objects can only be manipulated via references, which can be stored in variables. An object can have several references, often called its aliases. The object can be manipulated via any one of its aliases.

// Create two distinct stacks of chars.
CharStack stackA = new CharStack(12); // Stack length: 12 chars
CharStack stackB = new CharStack(6); // Stack length: 6 chars

stackB = stackA; // (1) aliases after assignment
// Stack previously referenced by stackB can now be garbage collected.

Two stacks are created in the code above. Before the assignment at (1), the situation is as depicted in Figure . After the assignment at (1), reference variables stackA and stackB will denote the same stack, as depicted in Figure . Reference variables stackA and stackB are aliases after the assignment, as they refer to the same object. What happens to the stack object that was denoted by the reference variable stackB before the assignment? When objects are no longer in use, their memory is, if necessary, reclaimed and reallocated for other objects. This is called automatic garbage collection. Garbage collection in Java is taken care of by the runtime system.