Table of ContentsArraysExceptions

Advanced Object-Oriented Programming

Earlier in this appendix you learned the basics of object-oriented programming, including how an abstract concept is encapsulated as a class through the use of Java fields and methods. As you can imagine (or you might already know), there's much more to object-oriented programming than encapsulation. In the next section, you'll look at the power of object relationships with inheritance, object scope, and visibility, and you'll learn a few other tricks along the way.

Basic Inheritance

Suppose you're working on a racing game involving many different types of cars and trucks, all tearing around a racetrack. To program this game using encapsulation, you would likely create a class to represent a Car type object and another for the Truck type object. As you wrote the code for these classes, you'd quickly find that a great deal of the functionality is similar, if not exactly the same. Both objects move around the track; even though they move at different speeds with different physics, these are only minor (parametized) differences. There are plenty of other similarities as well. Couldn't you (or shouldn't you) make these the same class? The problem is that they aren't the same type. Although much of the code is similar, there are still significant differences for each of these types.

The solution is to abstract out the shared components among these types into a shared class. Java then lets you have your final Car and Truck objects inherit this shared base of functionality. The name and type of this new base class is completely arbitrary; it's up to you what functionality you move into this class. For your Car and Truck class, you can say that these classes share their movement code but not their drawing code, so you'll create another Vehicle class to contain this.

Now I want you to stop for a second and think about this. I've found that a common misconception with inheritance is thinking there is a relationship between classes beyond what you define. Keep in mind that this is a completely abstract process. Given that you've only put the shared movement code into the Vehicle class, you don't necessarily have to call it a Vehicle.A better name is possibly the more abstract Mover class, which more adequately describes the limited functionality (see Figure A.3).

Figure Figure A.3. The common components (movement) of your Car and Truck classes are abstracted into a Mover class.

graphic/apaicon03.gif


Take a look at the code for this:

class Mover
{
    protected int x, y;

    public Mover()
    {
        x = 100;
        y = 100;
    }

    public void move(int newX, int newY)
    {
        x = newX;
        y = newY;
    }
}

class Car extends Mover
{
    public Car()
    {
    }

    public void draw()
    {
        // draw code
    }
}

The new addition here is the extends statement in the declaration of the Car class. This causes the class object to inherit all the functionality of the Mover class, which means you can now use the Mover class methods as though they were in the Car class. For example:

Car c = new Car();
c.move(10, 10);
System.out.println("Car position is " + c.x + ", " + c.y);

There's actually a subtle bug in the code for the previous example. The problem is that the Car object now encapsulates two classes. So when you instantiate the object, which constructor gets calledCar's or Mover's? The answer is Car's,which means the Mover con-structor code to initialize the x and y positions is never called. To get around this, you use the super keyword to implicitly call the base class constructor.

class Mover
{
    protected int x, y;

    public Mover()
    {
        x = 100;
        y = 100;
    }

    public void move(int newX, int newY)
    {
        x = newX;
        y = newY;
    }
}

class Car extends Mover
{
    public Car()
    {
        super();
    }

    public void draw()
    {
        // draw code
    }
}

The Car class constructor now calls the Mover constructor using the super method.

Now I want to tell you about one of the great powers of inheritance. Imagine you have a collection of all these Mover class objects (Car and Truck). You can now call the Mover class methods on any of the instantiated subclass objects. Still with me? Take a look at this in action:

Vector allMovers;

public void startGame()
{
    // create our collection of movers
    allMovers = new Vector();

    // construct and add a car and truck to the collection
    allMovers.addElement( new Car() );
    allMovers.addElement( new Truck() );
}

public void moveAll(int x, int y)
{
    // loop through all our movers and call their move methods
    for (int i=0; i < allMovers.length(); i++)
    {
        Mover m = (Mover) allMovers.elementAt(i);
        m.move(x, y);
    }
}

As you can see, I've added a moveAll method that calls the Mover class's move method for all the Mover-derived objects, even though some of those are Car and Truck objects.

This example also begs the question, how do you know what type an object really is? To determine an object type at run time, use the instanceof operator. For example, this modified version of the moveAll method will only move Car class objects:

public void moveAll(int x, int y)
{
    // loop through all our movers and call their move methods
    for (int i=0; i < allMovers.length(); i++)
    {
        Mover m = (Mover) allMovers.elementAt(i);
        if (m instanceof Car)
            m.move(x, y);
    }
}

Object Base

It might also surprise you to find that you're using a form of inheritance with every object you create. All Java classes are automatically derived from the java.lang.Object class, which means they also inherit all the functionality of this class.

One nice available method is getClass,which returns a Class object representing the class of a particular object. For example, rewrite the moveAll method from the previous example, but instead of using the instanceof operator, get the Class object and compare its name to the one you want.

public void moveAll(int x, int y)
{
    // loop through all our movers and call their move methods
    for (int i=0; i < allMovers.length(); i++)
    {
        Mover m = (Mover) allMovers.elementAt(i);
        if (m.getClass().getName().equals("Car"))
            m.move(x, y);
    }
}

Method Overriding

Suppose you have a method in a base class that in some circumstances isn't quite appropriate. For example, imagine your Truck class has a slightly different version of movement, such as a speed limitation. In this case you can override the method in the base class with a more specific one.

class Truck extends Mover
{
    public Truck()
    {
        super();
    }

    public void move(int x, int y)
    {
        super.move(x, y);

        if (x > 10)
            x = 10;
    }
}

Notice the use of super again. What you've done here is create a new Truck class that extends the Mover object, just like Car, except you also implement a method exactly matching the base class's move method signature. This overridden version then calls the base class's version before it finally carries out a simple x position test and adjustment. From now on any call to the Truck object's move method will use this more specialized version.

Abstract

Consider for a minute the nature of the Mover class you've used in previous examples. It's not really anything concrete or usable, is it? In fact, you designed it to be a base class with no intention of ever instantiating it as an object. However, the way you've coded it now, any user of your racing game class library would be able to create a Mover object, even though this wasn't your intention. You can use the abstract keyword in the declaration of a class to make this impossible. Here's how you can enforce your design intention:

abstract class Mover
{
    ...
}

Mover m = new Mover();   // ERROR! Abstract class

The Mover object can no longer be constructed directly.

You can also use the abstract keyword on a method, but it has a slightly different meaning. Suppose later on you decide to enforce that every Mover-derived class must include a draw method. You can do this by adding an abstract method to the base class. This also saves you from having to implement anything for that method in the base class. For example:

class Mover
{
    ...

    abstract public void draw();     // abstract method; no body required
}

class Car extends Mover
{
    public void draw()
    {
        // draw code
    }
}

class Truck extends Mover
{
    ...

    // Error! Class must include a draw method
}

Interfaces

Up until now you've only seen examples of single inheritance, but imagine if you had a case in which you wanted to extend from more than one base class. For instance, you could create a Tank class by extending both the Mover and a new base class Firer. Deriving from more than one base class is known as multiple inheritance.

Java does not support a direct form of multiple inheritance, so you can't do something like this:

class Tank extends Mover, Firer     // Error!

In Java, you can't ever have a case where an object has a split class type; there can only ever be one parent type for any class. You can, however, make a class "look" like another one by using interfaces.

You can think of an interface as a completely abstract class. It has no fields (except static ones, but I'll get to that a little later), and all the methods are inherently abstract, with no code bodies. (Why didn't the abstract method go to the ball? Because it didn't have anybody to dance with! Sad, aren't I?)

Interfaces are really compiler tools that enforce that a class implements a known set of methods (or interfaces). You can create an interface in much the same way as you create a completely abstract base class. I want to revisit the Tank class again, this time using an interface:

class Mover
{
    ...
    abstract public void draw();
}

interface Firer
{
    ...
    abstract public void fire();
}

class Tank extends Mover, implement Firer
{
    ...

    public void move(int x, int y)
    {
        ...
    }

    public void fire()  // we now "comply" with the Firer interface
    {
        ...
    }
}

Now you can use the Tank class as both a Mover and a Firer.For example, here's a revised moveAll method from previous examples, except this time you have any Tank objects open fire as well:

public void moveAll(int x, int y)
{
    // loop through all our movers and call their move methods
    for (int i=0; i < allMovers.length(); i++)
    {
        Mover m = (Mover) allMovers.elementAt(i);
        if (m instanceof Mover)
            m.move(x, y);

        if (m instanceof Firer)
            m.fire();
    }
}

Notice that I'm checking whether the object m is an instance of the Firer interface. This is an important distinction; the object can look like many different types, but it is still actually only one type.

Visibility

By default, when you declare a field or method in a class, it is only visible (and thus accessible) to the code within members of that class; thus, it is private to that class. If you want to gain access to the declaration in a derived class, you can do so by declaring the field or method to be protected. If you want to allow any class access to the method or field, you can declare it as public. For example:

class Mover
{
    protected int x, y;  // x and y are now available to derived classes

    public Mover()
    {
        x = 100;
        y = 100;
    }

    public void move(int newX, int newY)
    {
        x = newX;
        y = newY;
    }
}

class Car extends Mover
{
    public Car()
    {
        super();
    }

    private void setup()
    {
        x = 50;        // this is now legal
        y = 50;
    }
}

In this example, the visibility of the two base class integers x and y is protected, so the direct access to these fields in the derived Car class setup method is now valid.

NOTE

Tip

It is generally a good idea to limit the visibility of a method to private if it's intended for use only within a class (and is never called from outside the class). The same applies to protected scope; use it for methods that are used within a hierarchy, but which are not intended for use by outside (public) classes.

Inner Classes

In Java, you can declare classes within classes by creating inner classes. Typically, you create an inner class in a situation where you need a class but it exists only as a helper to another class. This is especially true if you do not intend to use the inner class by itself.

An inner class has access to all the fields and methods of its parent class (yes, even private ones). However, the parent class cannot access any private parts of the inner class.

As you can see in the following example, an inner class often serves to encapsulate components of the greater class.

class Car
{
    Color myColor;

    public Car(Color color)
    {
    myColor = color;
    }

    private void setup()
    {
        x = 50;          // this is now legal
        y = 50;
    }

    // inner class encapsulates color
    class Color
    {
        int r;
        int g;
        int b;

        public Color(int ar, int ag, int ab)
        {
            r = ar;
            g = ag;
            b = ab;
        }

        ...
    }
}

// application code...
{
    Car.Color red = new Car.Color(255, 0, 0);
    Car c = new Car( red );
}

Finalize

Earlier in this appendix, I reviewed the role of the garbage collector in cleaning up objects that are no longer referenced by your application. However, before the garbage collector destroys the object, you have an opportunity to do a little processing by implementing a finalize method.

NOTE

Tip

After the call to finalize, the garbage collector will recheck the reference count for the object. If the reference is no longer zero, the garbage collector aborts the destruction of the object. Knowing this, it's possible to abort the destruction of an object simply by retaining a reference to it in your finalize method. (At least it's possible in theory.)

In practice, I don't recommend doing this. Although it's perfectly legal, you really shouldn't be at a point in your code where you have to rely on a trick like this. Just keep a reference on objects you don't want dumped, and they'll stay around as long as you want.

The finalize method is easy to add. Just override the method (which is declared in the Object base class) and add any code you want, such as code to free up resources. Here's an example:

class Car extends Mover
{
    public Car()
    {
        super();
    }

    protected void finalize() throws Throwable
    {
        // cleanup code
    }

    ...
}

NOTE

Tip

If you're thinking you can move all that resource cleanup code into finalize methods, then I'm sorryyou're out of luck. The CLDC does not support finalize. Even if it did, under normal circumstances you should not rely on it as a means of cleaning up after yourself. Free up resources when you drop the reference to the object; there's no reason to rely on finalize.

This

The this reference is a special variable. It is accessible from within any class, and it represents the instance of the object itself. You can use this to check whether an object is accessing itself. For example:

public boolean isCollidingWith(Object another)
{
    if (this == another)     // can't collide with yourself
        return false;

    return myRect.intersects( another.getRectangle() );
}

You can also use this to access a class member, especially fields. This might be useful if you need to override visibility. For example:

class Test
{
    int i;

    public void setI(int i)   // argument has same name as field
    {
        this.i = i;  // using this to override default scope
    }
}

Statics

There are two types of statics in Javafields and class initializers. Static fields differ from normal fields in that they share among all instances (objects) of that class, no matter how many objects the class contains. For instance, suppose you wanted to keep track of the number of Car objects you construct. Having a field in the Car class itself isn't going to be much help. For example:

public class Car
{
    private int totalCars;

    public Car()
    {
        totalCars++;
    }

    public int getTotalCars()
    {
        return totalCars;
    }
}

... // application code
{
    Car c1 = new Car();
    Car c2 = new Car();

    System.out.println(" total cars = " + c1.getTotalCars());
}

The intention of this code is to keep track of the total number of Car objects you have constructed. However, since the totalCars field is an instance field, the JVM will create a new copy every time you construct a new object; totalCars will never be higher than one. An alternative would be to move the totalCars variable to an outer scope. For example:

public class Car
{
    public Car()
    {
    }
}

... // application code
{
    int totalCars=0;

    Car c1 = new Car();
    totalCars++;

    Car c2 = new Car();
    totalCars++;

    System.out.println(" total cars = " + totalCars);
}

This solution now works, but it's not quite what you wanted. For a start, you've (arguably) broken the encapsulation of your Car class, and you now need to add code to increment (and later decrement) the car counter wherever you construct one. What you need is something in between, such as a variable that exists for all instances of a particle class. In Java, you can do this by declaring a field to be static.Here's the static version of your car counter:

public class Car
{
    private static int totalCars;          // now a static "class" field

    public Car()
    {
        totalCars++;
    }

    public int getTotalCars()
    {
        return totalCars;
    }
}

... // application code
{
    Car c1 = new Car();
    Car c2 = new Car();

    System.out.println(" total cars = " + c1.getTotalCars());
}

As you can see, I've changed the totalCars field declaration to include the static keyword, thus making it a class field. The totalCars++ increment code is now the same across all Car classes.

There's still one other problem, though. What if you want to access the number of cars from a different section of your game? Or what if you want to determine how many cars there are without having to actually construct any cars at all? Because you need to obtain a Car object before you can call the method, you will always need to have at least one Car object to call the getTotalCars method. This also seems a little silly. Why do you need an object to get access to a static field, since the totalCars value is not specific to an object? The answer is that you don'twhat you need is to create a static method for the Car class.

public class Car
{
    private static int totalCars;          // now a class field

    ...
    public static int getTotalCars()     // static method
    {
        return totalCars;   // access to a static member is ok
    }
}

... // application code to get total cars
{
    System.out.println(" total cars = " + Car.getTotalCars());
}

The getTotalCars method is now static, so the application code can call using the Car class (Car.getTotalCars) rather than the object. In fact, a Car object is never constructed in the preceding code, so the result (correctly) is total cars = 0.

One final thing to keep in mind when using static methods is that the code cannot reference any object field directly. (Which one would it access, anyway?) Therefore, the following code is not legal:

public class Car
{
    private static int totalCars;          // static field
    private int color;                     // non-static field
    ...

    public static int getTotalCars()       // static method
    {
        if (totalCars > 10)
            color = 0xFF0000;                // Error, non-static field!

        return totalCars;
    }
}

NOTE

Tip

Static methods are cleaner, faster, and easier to access than object methods. When you're adding a new method to a class, always ask yourself whether it can be a static method. You might be surprised by how many methods work just as well as statics.

Final

The final keyword tells the compiler that a particular element is immutable (in other words, it can't be changed). This is useful for both efficiency (the compiler can dramatically optimize methods or fields declared final) or because you want to make sure no extending class is able to override something. Here's an example:

public class Car
{
    private static final int DEFAULT_SPEED = 10;

    private int speed;

    public Car()
    {
        speed = DEFAULT_SPEED;
    }
}

Here I've used a final integer to improve the readability of the class. (Note that it's a common practice to use all uppercase in final fields.) You also have the option of initializing a final at a later stage, but the compiler will enforce that this initialization occurs before any use. For example:

public class Car
{
    private static final int DEFAULT_SPEED;     // blank final

    private int speed;

    public Car(int startingSpeed)
    {
        if (DEFAULT_SPEED == 0)
            DEFAULT_SPEED = startingSpeed;
        speed = DEFAULT_SPEED;
    }
}

The final keyword is available for methods as well. This has two effects. First, declaring a method final means that no subclass can override it, which might be a part of your class design. Second, the compiler might optionally inline a final method. (If you're familiar with C++, you likely know what I'm talking about already.) Inlining means the compiler can place a method directly into the calling area, instead of making a slower call to the method's code. This can have dramatic performance results.

    Table of ContentsArraysExceptions