Section 5.6
Interfaces, Nested Classes, and Other Details

THIS SECTION simply pulls together a few more miscellaneous features of object oriented progrmming in Java. Read it now, or just look through it and refer back to it later when you need this material.


Interfaces

Some object-oriented programming languages, such as C++, allow a class to extend two or more superclasses. This is called multiple inheritance. In the illustration below, for example, class E is shown as having both class A and class B as direct superclasses, while class F has three direct superclasses.

Such multiple inheritance is not allowed in Java. The designers of Java wanted to keep the language reasonably simple, and felt that the benefits of multiple inheritance were not worth the cost in increased complexity. However, Java does have a feature that can be used to accomplish many of the same goals as multiple inheritance: interfaces.

We've encountered the term "interface" before, in connection with black boxes in general and subroutines in particular. The interface of a subroutine consists of the name of the subroutine, its return type, and the number and types of its parameters. This is the information you need to know if you want to call the subroutine. A subroutine also has an implementation: the block of code which defines it and which is executed when the subroutine is called.

In Java, interface is a reserved word with an additional, technical meaning. An "interface" in this sense consists of a set of subroutine interfaces, without any associated implementations. A class can implement an interface by providing an implementation for each of the subroutines specified by the interface. Here is an example of a very simple Java interface:

       public interface Drawable {
          public void draw(Graphics g);
       }

This looks much like a class definition, except that the implementation of the method draw() is omitted. A class that implements the interface, Drawable, must provide an implementation for this method. Of course, the class can also include other methods and variables. For example,

        class Line implements Drawable {
            public void draw(Graphics g) {
                . . . // do something -- presumably, draw a line
            }
            . . . // other methods and variables
         }

Any class that implements the Drawable interface defines a draw() instance method. Any object created from such a class includes a draw() method. We say that an object implements an interface if it belongs to a class that implements the interface. For example, any object of type Line implements the Drawable interface. Note that it is not enough for the object to include a draw() method. The class that it belongs to has to say that it "implements Drawable.

While a class can extend only one other class, it can implement any number of interfaces. In fact, a class can both extend another class and implement one or more interfaces. So, we can have things like

        class FilledCircle extends Circle 
                                implements Drawable, Fillable {
           . . .
        }

The point of all this is that, although interfaces are not classes, they are something very similar. An interface is very much like an abstract class, that is, a class that can never be used for constructing objects, but can be used as a basis for making subclasses. The subroutines in an interface are abstract methods, which must be implemented in any concrete class that implements the interface. And as with abstract classes, even though you can't construct an object from an interface, you can declare a variable whose type is given by the interface. For example, if Drawable is an interface, and if Line and FilledCircle are classes that implement Drawable, then you could say:

       Drawable figure;  // Declare a variable of type Drawable.  It can
                         //    refer to any object that implements the
                         //    Drawable interface.
                         
       figure = new Line();  // figure now refers to an object of class Line
       figure.draw(g);   // calls draw() method from class Line
       
       figure = new FilledCircle();   // Now, figure refers to an object
                                      //   of class FilledCircle.
       figure.draw(g);   // calls draw() method from class FilledCircle

A variable of type Drawable can refer to any object of any class that implements the Drawable interface. A statement like figure.draw(g), above, is legal because figure is of type Drawable, and any Drawable object has a draw() method.

Note that a type is something that can be used to declare variables. A type can also be used to specify the type of a parameter in a subroutine, or the return type of a function. In Java, a type can be either a class, an interface, or one of the eight built-in primitive types. These are the only possibilities. Of these, however, only classes can be used to construct new objects.

You are not likely to need to write your own interfaces until you get to the point of writing fairly complex programs. However, there are a few interfaces that are used in important ways in Java's standard packages. You'll learn about some of these standard interfaces in the next few chapters.


Nested Classes

A class seems like it should be a pretty important thing. A class is a high-level building block of a program, representing a potentially complex idea and its associated data and behaviors. I've always felt a bit silly writing tiny little classes that exist only to group a few scraps of data together. However, such trivial classes are often useful and even essential. Fortunately, in Java, I can ease the embarrassment, because one class can be nested inside another class. My trivial little class doesn't have to stand on its own. It becomes part of a larger more respectable class. This is particularly useful when you want to create a little class specifically to support the work of a larger class. And, more seriously, there are other good reasons for nesting the definition of one class inside another class.

In Java, a nested class or inner class is any class whose definition is inside the definition of another class. Inner classes can be either named or anonymous. I will come back to the topic of anonymous classes later in this section. A named inner class looks just like any other class, except that it is nested inside another class. (It can even contain further levels of nested classes, but you shouldn't carry these things too far.)

Like any other item in a class, a named inner class can be either static or non-static. A static nested class is part of the static structure of the containing class. It can be used inside that class to create objects in the usual way. If it has not been declared private, then it can also be used outside the containing class, but when it is used outside the class, its name must indicate its membership in the containing class. This is similar to other static components of a class: A static nested class is part of the class itself in the same way that static member variables are parts of the class itself.

For example, suppose a class named WireFrameModel represents a set of lines in three-dimensional space. (Such models are used to represent three-dimensional objects in graphics programs.) Suppose that the WireFrameModel class contains a static nested class, Line, that represents a single line. Then, outside of the class WireFrameModel, the Line class would be referred to as WireFrameModel.Line. Of course, this just follows the normal naming convention for static members of a class. The definition of the WireFrameModel class with its nested Line class would look, in outline, like this:

            public class WireFrameModel {
            
               . . . // other members of the WireFrameModel class
               
               static public class Line {
                     // Represents a line from the point (x1,y1,z1)
                     // to the point (x2,y2,z2) in 3-dimensional space.
                  double x1, y1, z1;
                  double x2, y2, z2;
               } // end class Line
               
               . . . // other members of the WireFrameModel class
               
            } // end WireFrameModel

Inside the WireframeModel class, a Line object would be created with the constructor "new Line()". Outside the class, "new WireFrameModel.Line()" would be used.

A static nested class has full access to the members of the containing class, even to the private members. This can be another motivation for declaring a nested class, since it lets you give one class access to the private members of another class without making those members generally available to other classes.

When you compile the above class definition, two class files will be created. Even though the definition of Line is nested inside WireFrameModel, the compiled Line class is stored in a separate file. The name of the class file for Line will be WireFrameModel$Line.class.


Non-static nested classes are not, in practice, very different from static nested classes, but a non-static nested class is actually associated to an object rather than to the class in which it is nested. This can take some getting used to.

Any non-static member of a class is not really part of the class itself (although its source code is contained in the class definition). This is true for non-static nested classes, just as it is for any other non-static part of a class. The non-static members of a class specify what will be contained in objects that are created from that class. The same is true -- at least logically -- for non-static nested classes. It's as if each object that belongs to the containing class has its own copy of the nested class. This copy has access to all the instance methods and instance variables of the object. Two copies of the nested class in different objects differ because the instance variables and methods they refer to are in different objects. In fact, the rule for deciding whether a nested class should be static or non-static is simple: If the class needs to use any instance variable or instance method, make it non-static. Otherwise, it might as well be static.

From outside the containing class, a non-static nested class has to be referred to as variableName.NestedClassName, where variableName is a variable that refers to the object that contains the class. This is actually rather rare, however. A non-static nested class is generally used only inside the class in which it is nested, and there it can be referred to by its simple name.

In order to create an object that belongs to a non-static nested class, you must first have an object that belongs to the containing class. (When working inside the class, the object "this" is used implicitly.) The nested class object is permanently associated with the containing class object, and it has complete access to the members of the containing class object. Looking at an example will help, and will hopefully convince you that non-static nested classes are really very natural. Consider a class that represents poker games. This class might include a nested class to represent the players of the game. This structure of the PokerGame class could be:

         class PokerGame {  // Represents a game of poker.
             
             class Player {  // Represents one of the players in this game.
                .
                .
                .
             } // end class Player
             
             private Deck deck;      // A deck of cards for playing the game.
             private int pot;        // The amount of money that has been bet.
             
             .
             .
             .
         
         } // end class PokerGame

If game is a variable of type PokerGame, then, conceptually, game contains its own copy of the Player class. In an an instance method of a PokerGame object, a new Player object would be created by saying "new Player()", just as for any other class. (A Player object could be created outside the PokerGame class with an expression such as "new game.Player()". Again, however, this is rather rare.) The Player object will have access to the deck and pot instance variables in the PokerGame object. Each PokerGame object has its own deck and pot and Players. Players of that poker game use the deck and pot for that game; playes of another poker game use the other game's deck and pot. That's the effect of making the Player class non-static. This is the most natural way for players to behave. A Player object represents a player of one particular poker game. If Player were a static nested class, on the other hand, it would represent the general idea of a poker player, independent of a particular poker game.


In some cases, you might find yourself writing a nested class and then using that class in just a single line of your program. Is it worth creating such a class? Indeed, it can be, but for cases like this you have the option of using an anonymous nested class. An anonymous class is created with a variation of the new operator that has the form

          new  superclass-or-interface () {
                   methods-and-variables
              }

This constructor defines a new class, without giving it a name, and it simultaneously creates an object that belongs to that class. This form of the new operator can be used in any statement where a regular "new" could be used. The intention of this expression is to create: "a new object belonging to a class that is the same as superclass-or-interface but with these methods-and-variables added." The effect is to create a uniquely customized object, just at the point in the program where you need it. Note that it is possible to base an anonymous class on an interface, rather than a class. In this case, the anonymous class must implement the interface by defining all the methods that are declared in the interface.

Anonymous classes are most often used for handling events in graphical user interfaces, and we will encounter them several times in the next two chapters. For now, we will look at one not-very-plausible example. Consider the Drawable interface, which is defined earlier in this section. Suppose that we want a Drawable object that draws a filled, red, 100-pixel square. Rather than defining a separate class and then using that class to create the object, we can use an anonymous class to create the object in one statement:

              Drawable redSquare = new Drawable() {
                     void draw(Graphics g) {
                        g.setColor(Color.red);
                        g.fillRect(10,10,100,100);
                     }
                 };

The semicolon at the end of this statement is not part of the class definition. It's the semicolon that is required at the end of every declaration statement.

When a Java class is compiled, each anonymous nested class will produce a separate class file. If the name of the main class is MainClass, for example, then the names of the class files for the anonymous nested classes will be MainClass$1.class, MainClass$2.class, MainClass$3.class, and so on.


More about Access Modifiers

A class can be declared to be public. A public class can be accessed from anywhere. Certain classes have to be public. A class that defines a stand-alone application must be public, so that the system will be able to get at its main() routine. A class that defines an applet must be public so that it can be used by a Web browser. If a class is not declared to be public, then it can only be used by other classes in the same "package" as the class. Packages are discussed in Section 4.5. Classes that are not explicitly declared to be in any package are put into something called the default package. All the examples in this textbook are in the default package, so they are all accessible to one another whether or not they are declared public. So, except for applications and applets, which must be public, it makes no practical difference whether our classes are declared to be public or not.

However, once you start writing packages, it does make a difference. A package should contain a set of related classes. Some of those classes are meant to be public, for access from outside the package. Others can be part of the internal workings of the package, and they should not be made public. A package is a kind of black box. The public classes in the package are the interface. (More exactly, the public variables and subroutines in the public classes are the interface). The non-public classes are part of the non-public implementation. Of course, all the classes in the package have unrestricted access to one another.

Following this model, I will tend to declare a class public if it seems like it might have some general applicability. If it is written just to play some sort of auxiliary role in a larger project, I am more likely not to make it public.

A member variable or subroutine in a class can also be declared to be public, which means that it is accessible from anywhere. It can be declared to be private, which means that it accessible only from inside the class where it is defined. Making a variable private gives you complete control over that variable. The only code that will ever manipulate it is the code you write in your class. This is an important kind of protection.

If no access modifier is specified for a variable or subroutine, then it is accessible from any class in the same package as the class. As with classes, in this textbook there is no practical difference between declaring a member public and using no access modifier at all. However, there might be stylistic reasons for preferring one over the other. And a real difference does arise once you start writing your own packages.

There is a third access modifier that can be applied to a member variable or subroutine. If it is declared to be protected, then it can be used in the class where it is defined and in any subclass of that class. This is obviously less restrictive than private and more restrictive than public. Classes that are written specifically to be used as a basis for making subclasses often have protected members. The protected members are there to provide a foundation for the subclasses to build on. But they are still invisible to the public at large.


Mixing Static and Non-static

Classes, as I've said, have two very distinct purposes. A class can be used to group together a set of static member variables and static member subroutines. Or it can be used as a factory for making objects. The non-static variables and subroutine definintions in the class definition specify the instance variables and methods of the objects. In most cases, a class performs one or the other of these roles, not both.

Sometimes, however, static and non-static members are mixed in a single class. In this case, the class plays a dual role. Sometimes, these roles are completely separate. It is also possible for the static and non-static parts of a class to interact. This happens when instance methods use static member variables or call static member subroutines. An instance method belongs to an object, not to the class itself, and there can be many objects with their own versions of the instance method. But there is only one copy of a static member variable. So, effectively, we have many objects sharing that one variable.

Suppose, for example, that we want to write a PairOfDice class that uses the Random class mentioned in Section 3 for rolling the dice. To do this, a PairOfDice object needs access to an object of type Random. But there is no need for each PairOfDice object to have a separate Random object. (In fact, it would not even be a good idea: Because of the way random number generators work, a program should, in general, use only one source of random numbers.) A nice solution is to have a single Random variable as a static member of the PairOfDice class, so that it can be shared by all PairOfDice objects. For example:

         class PairOfDice {
         
            private static Random randGen = new Random();
               // (Note:  Assumes that java.util.Random has been imported.)
            
            public int die1;   // Number showing on the first die.
            public int die2;   // Number showing on the second die.
            
            public PairOfDice() {
                    // Constructor.  Creates a pair of dice that
                    // initially shows random values.
                 roll();
            }

            public void roll() {
                    // Roll the dice by setting each of the dice to be
                    // a random number between 1 and 6.
                 die1 = randGen.nextInt(6) + 1;
                 die2 = randGen.nextInt(6) + 1;
            }
            
        } // end class PairOfDice

As another example, let's rewrite the Student class that was used in the Section 2. I've added an ID for each student and a static member called nextUniqueID. Although there is an ID variable in each student object, there is only one nextUniqueID variable.

        public class Student {
        
           private String name;  // Student's name.
           private int ID;  // Unique ID number for this student.
           public double test1, test2, test3;   // Grades on three tests.
           
           private static int nextUniqueID = 0;
                     // keep track of next available unique ID number
           
           Student(String theName) {
                // Constructor for Student objects;
                // provides a name for the Student,
                // and assigns the student a unique
                // ID number.
              name = theName;
              nextUniqueID++;
              ID = nextUniqueID;
           }
           
           public String getName() {
                // Accessor method for reading value of private
                // instance variable, name.
              return name;
           }
           
           public int getID() {
                // Accessor method for reading value of ID.
              return ID;
           }
           
           public double getAverage() {  
                // Compute average test grade.
              return (test1 + test2 + test3) / 3;
           }
           
        }  // end of class Student

The initialization "nextUniqueID = 0" is done only once, when the class is first loaded. Whenever a Student object is constructed and the constructor says "nextUniqueID++;", it's always the same static member variable that is being incremented. When the very first Student object is created, nextUniqueID becomes 1. When the second object is created, nextUniqueID becomes 2. After the third object, it becomes 3. And so on. The constructor stores the new value of nextUniqueID in the ID variable of the object that is being created. Of course, ID is an instance variable, so every object has its own individual ID variable. The class is constructed so that each student will automatically get a different value for its ID variable. Furthermore, the ID variable is private, so there is no way for this variable to be tampered with after the object has been created. You are guaranteed, just by the way the class is designed, that every student object will have its own permanent, unique identification number. Which is kind of cool if you think about it.



End of Chapter 5

[ Next Chapter | Previous Section | Chapter Index | Main Index ]