Previous Page TOC Next Page



- 18 -
Packages, Interfaces, and Exception Handling


Packages and interfaces are two capabilities that allow you greater control and flexibility in designing sets of interrelated classes. Packages allow you to combine groups of classes and control those classes available to the outside world; interfaces are a way of grouping abstract method definitions and sharing them among classes that might not necessarily acquire those methods through inheritance.

Exception handling provides a method for dealing with unexpected events that interrupt the normal operation of the program. There are specific statements built into the Java language to handle exceptions.

Today, you'll cover how to design with, use, and create your own packages, interfaces, and exception handlers. Specific topics you'll learn today include the following:


Programming in the Large and Programming in the Small


When you examine a new language feature, you should ask yourself two questions:

  1. How can I use it to better organize the methods and classes of my Java program?
  2. How can I use it while writing the actual Java code?

The first is often called programming in the large, and the second, programming in the small. Bill Joy, a founder of Sun Microsystems, likes to say that Java feels like C when programming in the small and like Smalltalk when programming in the large. What he means is that while you're coding individual lines, Java is familiar and powerful like any C-like language, but while you're designing, has the extensibility and expressive power of a pure object-oriented language such as Smalltalk.

The separation of designing from coding was one of the most fundamental advances in programming in the past few decades. Object-oriented languages such as Java implement a strong form of this separation. When you develop a Java program, you first design the classes and decide on the relationships between these classes; you then implement the Java code needed for each of the methods in your design. If you are careful enough with both of these processes, you can change your mind about aspects of the design without affecting anything but small, local pieces of your Java code. You can also change the implementation of any method without affecting the rest of the design.

As you begin to explore more advanced Java programming, however, you'll find that this simple model becomes too limiting. Today, you'll explore these limitations of programming in the large and in the small, which motivate the need for packages and interfaces. First, start with packages.

What Are Packages?


Packages, as I've already mentioned in this book, are a way of organizing groups of classes. For example, the AWT package contains all the classes that are related to user interface design. A package contains any number of classes that are related in purpose, in scope, or by inheritance.

Why bother with packages? If your programs are small and use a limited number of classes, you might find that you don't need to explore packages at all. But the more Java programming you do, the more classes you'll end up with. And, although those classes might be individually well-designed, reusable, encapsulated, and with specific interfaces to other classes, you might find the need for a bigger organizational entity that allows you to group your classes.

Packages are useful for several reasons:

While a package is usually a collection of classes, packages can also contain other packages, forming yet another level of organization somewhat analogous to the inheritance hierarchy. Each level usually represents a smaller, more specific grouping of classes. The Java class library itself is organized along these lines. The top level is called java; the next level includes names such as io, net, util, and awt. The last of these has an even lower level, which includes the package image.



By convention, the first level of the hierarchy specifies the (globally unique) name to identify the author or owner of those packages. For example, Sun Microsystem's classes, which are not part of the standard Java environment, all begin with the prefix sun. Netscape includes classes with its implementation that are contained in the netscape package. The standard package, java, is an exception to this rule because it is so fundamental and it might someday be implemented by multiple companies.

More about package naming conventions later today when you create your own packages.



Using Packages


You've been using packages all along in this book. Every time you used the import command and referred to a class by its full package name (java.awt.Color, for example), you were using packages. Now I'll go into greater depth than in previous lessons by going over the specifics of how to use classes from other packages in your own programs (to make sure you've got it).

To use a class contained in a package, you can use one of three mechanisms:

What about your own classes in your own programs that don't belong to any package? The rule is that if you don't specifically define your classes to belong to a package, they're put into a default unnamed package. You can refer to those classes by class name from anywhere in your classes.

Full Package and Class Names


To refer to a class in some other package, you can use its full name: the class name preceded by any package names. You do not have to import the class or the package to use it in the following way:




java.awt.Font f = new java.awt.Font()

For classes that you use only once or twice in your program, using the full name makes the most sense. If, however, you use that class multiple times, or if the package name is really long with lots of subpackages, you'll want to, instead, import that class to save yourself some typing.

The import Command


To import classes from a package, use the import command as you've done throughout this book. You can import an individual class like the following:




import java.util.Vector;

Or you can import an entire package of classes using a * to replace the individual class names:




import java.awt.*


Actually, to be technically correct, this command doesn't import all the classes in a package; it only imports the classes that have been declared public. There is more on this in the section titled "Packages and Class Protection" later in this chapter.

Note that the asterisk (*) in this example is not like the one you might use at a command prompt to specify the contents of a directory or to indicate multiple files. For example, if you ask to list the contents of the directory classes/java/awt/*, that list includes all the .class files and subdirectories such as image and peer. Writing import java.awt.* does not import subpackages such as image and peer. To import all the classes in a complex package hierarchy, you must explicitly import each level of the hierarchy by hand. Also, you cannot indicate partial class names, for example, L* to import all the classes that begin with L. It's either all the classes in a package or a single class.

The import statements in your class definition, go to the top of the file before any class definitions (but after the package definition, as you'll see in the next section).

You might have noticed that the Visual J++ Applet Wizard automatically imports the java.applet.* and java.awt.* packages by adding the appropriate import statements at the top of the generated source code.

So, should you take the time to import classes individually or just import them as a group? It depends on how specific you want to be. Importing a group of classes does not slow down your program or make it any larger. But importing a package does make it a little more confusing for readers of your code to figure out where your classes are coming from. Using individual imports or importing packages is mostly a question of your own coding style.



Java's import command is not at all like the #include command in C-like languages. The C preprocessor takes the contents of all the included files (and, in turn, the files they include, and so on), and stuffs them at the spot where the #include was. The result is an enormous hunk of code that has far more lines than the original program did. Java's import tells the Java compiler and interpreter where (in which files) to find class, variable and method names and definitions. It doesn't bring anything into the current Java program.


Name Conflicts


Once you have imported a class or a package of classes, you can usually refer to a class name by its name without the package identifier. I say usually because there's one case where you might have to be more explicit: where there are multiple classes with the same name from different packages.

Here's an example. Suppose you import the classes from two packages from two different programmers (Joe and Eleanor):




import joesclasses.*;



import eleanorsclasses.*;

Inside Joe's package is a class called Name. Unfortunately, inside Eleanor's package there is also a class called Name that has an entirely different meaning and implementation. Who's version of Name ends up getting used if you refer to the Name class in your own program?




Name myName = new Name("Susan");

The answer is neither; the Java compiler will complain about a naming conflict and refuse to compile your program. In this case, despite the fact that you imported both classes, you still have to refer to the appropriate Name class by its full package name:




joesclasses.Name myName = new joesclasses.Name("Susan");

A Note About CLASSPATH and Where Classes Are Located


Before I go on to explain how to create your own packages of classes, I'd like to make a note about how Visual J++ finds packages and classes when it's compiling and running your classes.

For Visual J++ to be able to use a class, it has to be able to find it on the file system. Otherwise, you'll get an error that the class does not exist. Visual J++ uses two things to find classes: the package name itself and the directories listed in your CLASSPATH variable.

Package names map to directory names on the file system, so the class java.applet.Applet will actually be found in the applet directory, which in turn will be inside the java directory (java/applet/Applet.class, in other words).

Visual J++ then looks for those directories inside the directories listed in your CLASSPATH variable. The CLASSPATH variable is configured by selecting Settings from the Build menu. The CLASSPATH variable is defined under the tab General. Figure 18.1 shows the dialog box for setting the CLASSPATH variable. When you install Visual J++, the default location for the built-in classes is c:\windows\java\classes.

Figure 18.1. Setting the CLASSPATH variable.

Creating Your Own Packages


Creating your own packages is a difficult, involved process, involving many lines of code, long hours late at night with lots of coffee and the ritual sacrifice of many goats. Just kidding. To create a package of classes, you have three basic steps to follow: picking a package name, creating the directory structure, and using package to add your class to a package.

Picking a Package Name


The first step is to decide what the name of your package is going to be. The name you choose for your package depends on how you are going to be using those classes. Perhaps your package will be named after you, or perhaps after the part of the Java system you're working on (graphics or hardware_interfaces). If you're intending your package to be distributed to the Net-at-large, or as part of a commercial product, you'll want to use a package name (or set of package names) that uniquely identifies you or your organization or both.

One convention for naming packages that has been recommended by Sun is to use your Internet domain name, with the elements reversed. If you look in the c:\windows\java\classes directory, you will see that Microsoft has followed this suggestion and placed their classes in the com.ms structure. If your Internet domain name is fooblitzky.eng.nonsense.edu, your package name might be edu.nonsense.eng.fooblitzky. (You might add another package name onto the end of that to refer to the product or to you, specifically.)

The idea is to make sure your package name is unique. While packages can hide conflicting class names, the protection stops there. There's no way to make sure your package won't conflict with someone else's package if you both use the same common name.

By convention, package names tend to begin with a lowercase letter to distinguish them from class names. Therefore, for example, in the full name of the built-in String class, java.lang.String, it's easier to separate the package name from the class name visually. This convention helps reduce name conflicts.

Create the Directory Structure


Step two in creating packages is to create a directory structure on your disk that matches the package name. If your package has just one name (mypackage), you'll only have to create a directory for that one name. If the package name has several parts, however, you'll have to create directories within directories. For the package name edu.nonsense.eng.fooblitzky, you'll need to create an edu directory, create a nonsense directory inside edu, an eng directory inside nonsense, and a fooblitzky directory inside eng. Your classes and source files can then go inside the fooblitzky directory.

Use package to Add Your Class to a Package


The final step to putting your class inside packages is to add the package command to your source files. The package command says this class goes inside this package, and is used like the following:




package myclasses;



package edu.nonsense.eng.fooblitzky;



package java.awt;

The single package command, if any, must be the first line of code in your source file, after any comments or blank lines, and before any import commands.

As I mentioned earlier today, if your class doesn't have a package command in it, that class is contained in the default package, and can be used by any other class. But once you start using packages, you should make sure all your classes belong to some package to reduce the chance of confusion as to where your classes belong.

Creating Packages With the Visual J++ User Interface


The Visual J++ development environment will automate the preceding manual steps in creating your own packages. When you add a class using the graphical interface (as opposed to just typing it in), Visual J++ will automatically add the appropriate statements into your class and create the directory structure for you.

For example, if you want to create a new class called Dog that extends a class called Animal, you can simply right-click in the ClassView window and select Create New Class, as shown in Figure 18.2.

Figure 18.2. Creating a new class in Visual J++.

After you make this selection, a dialog box will appear that allows you to enter information about the class, such as the superclass, modifiers, and package information. In this case, name your class Dog and it will extend Animal. Also, create a package called myAnimals. Your new class, Dog, will be a member of the package myAnimals. The dialog box is shown in Figure 18.3.

Figure 18.3. The Create New Class dialog box.

After you enter the class information, Visual J++ will automatically create a source code template for the class with the appropriate statements for creating a package. In this case, you created a package called myAnimals. Figure 18.4 shows the template that was created for your new class called Dog. As you can see, the statement




package myAnimals;

was added to the top of the code. In addition, if you save the project and look at your file structure, you will see that the file Dog.class has been created in a directory called myAnimals.

Figure 18.4. The Source template for the Dog Class.

Packages and Class Protection


Yesterday you learned all about the four Ps of protection, and how they applied (primarily) to methods and variables and their relationship to other classes. When referring to classes and their relationship to other classes in other packages, you only have two Ps to worry about: package and public.

By default, classes have package protection, which means that the class is available to all the other classes in the same package, but is not visible or available outside that package (not even to subpackages). It cannot be imported or referred to by name; classes with package protection are hidden inside the package in which they are contained.

Package protection comes about when you define a class (as you have been doing throughout this book) like the following:




class TheHiddenClass extends AnotherHiddenClass {



...



}

To allow a class to be visible and importable outside your package, you'll want to make it have public protection by adding the public modifier to its definition:




public class TheVisibleClass {



...



}

public classes can be imported by other classes outside the package.

Note that when you use an import statement with an asterisk, you import only the public classes inside that package. Hidden classes remain hidden and are only used by the other classes in this package.

Why would you want to hide a class inside a package? For the same reason you want to hide variables and methods inside a class—so you can have utility classes and behavior that are only useful to your implementation, or so you can limit the interface of your program so that you can minimize the effect of larger changes. As you design your classes, you'll want to take the whole package into consideration and decide which classes will be public and which will be hidden.

Listing 18.1 shows two classes that illustrate this point. The first is a public class that implements a linked list; the second is a private node of that list.

Listing 18.1. LinkedList.




package  collections;



public class  LinkedList {



    private Node  root;



    public  void  add(Object o) {



        root = new Node(o, root);



    }



    . . .



}



class  Node {                   // not public



    private Object  contents;



    private Node    next;



    Node(Object o, Node n) {



        contents = o;



        next     = n;



    }



    . . .



}


You'll note that I'm breaking a rule I mentioned earlier in this book: you can only have one class definition per file. In truth, the restriction is that you can include as many class definitions per file as you want, but only one of them can be public. In reality, I find that keeping one class definition per file is easier to maintain because I don't have to go searching around for the definition of a class.

The public LinkedList class provides a set of useful public methods (such as add()) to any other classes that might want to use them. These other classes don't need to know about any support classes LinkedList needs to get its job finished. Node, which is one of those support classes, is therefore declared without a public modifier, and will not appear as part of the public interface to the collections class.



Just because Node isn't public doesn't mean LinkedList won't have access to it once its been imported into some other class. Think of protections not as hiding classes entirely, but more as checking the permissions of a given class to use other classes, variables, and methods. When you import and use LinkedList, the node class will also be loaded into the system. Only instances of LinkedList, though, will have permission to use it.

One of the great powers of hidden classes is that even if you use them to introduce a great deal of complexity into the implementation of some public class, all the complexity is hidden when that class is imported or used. Thus, creating a good package consists of defining a small, clean set of public classes and methods for other classes to use, and then implementing them by using any number of hidden (package) support classes.

What Are Interfaces?


Interfaces, like the abstract classes and methods you saw yesterday, provide templates of behavior that other classes are expected to implement. Interfaces, however, provide far more functionality to Java and to class and object design than simple abstract classes and methods. You will now explore interfaces: what they are, why they're crucial to getting the most out of the Java language for your own classes, and how to use and implement them.

The Problem of Single Inheritance


When you first began to design object-oriented programs, the concept of the class hierarchy probably seemed almost miraculous. Within that single tree, you can express a hierarchy of different types of objects, many simple to moderately complex relationships between objects and processes in the world, and any number of points along the axis, from abstract/general to concrete/specific. The strict hierarchy of classes appears, at first glance, to be simple, elegant, and easy to use.

After some deeper thought or more complex design experience, however, you might discover that the pure simplicity of the class hierarchy is restrictive, particularly when you have some behavior that needs to be used by classes in different branches of the same tree.

Now look at an example. Suppose you have a biological hierarchy, with Animal at the top, and the classes Mammal and Bird underneath. Things that define a mammal include bearing live young and having fur. Behavior or features of birds include having a beak and laying eggs. So far, so good, right? But how do you go about creating a class for the creature Platypus, which has fur, a beak, and lays eggs? You'd need to combine behavior from two classes to form the Platypus class. And, because classes can only have one immediate superclass in Java, this sort of problem cannot be solved elegantly.

Other OOP languages include the concept of multiple inheritance, which can solve this problem. With multiple inheritance, a class can inherit from more than one superclass, and get behavior and attributes from all its superclasses at once. Using multiple inheritance, you could simply factor the common behavior of egg laying creatures into a single class (EggLayers) and then create new classes that inherit from the primary superclass and the EggLayer class. For example, the Platypus class would inherit from both the Mammal class and the EggLayer class. In addition, a BeakedCreature class could be created, which could also be inherited by the Platypus class.

The problem with multiple inheritance is that it makes a programming language far more complex, both to learn, to use, and to implement. Questions of method invocation and how the class hierarchy is organized become far more complicated with multiple inheritance, and more open to confusion and ambiguity. And because one of the goals for Java was that it be simple, multiple inheritance was rejected in favor of the simpler single inheritance.

So, how do you solve the problem of needing common behavior that doesn't fit into the strict class hierarchy? Java, borrowing from Objective-C, has another hierarchy altogether. This is separate from the main class hierarchy of mixable behavior classes. When you create a new class, that class only has one primary superclass, but can pick and choose different common behaviors from the other hierarchy.

That other hierarchy is the interface hierarchy. A Java interface is a collection of abstract behaviors that can be mixed into any class to add behavior to that class that is not supplied by its superclasses. Specifically, a Java interface contains nothing but abstract method definitions and constants—no instance variables and no method implementations.

Interfaces are implemented and used throughout the Java class library whenever a behavior is expected to be implemented by a number of disparate classes. In the Java class hierarchy, for example, there are the interfaces, java.lang.Runnable, java.util.Enumeration, java.util.Observable, java.awt.image.ImageConsumer, and java.awt.image.ImageProducer. The interfaces in the standard Java class hierarchy might be useful to you in your own programs, so be sure and examine the API to see what's available to you.

Abstract Design and Concrete Implementation


Throughout this book you've gotten a taste of the difference between design and implementation in object-oriented programming, where the design of a thing is its abstract representation and its implementation is the concrete counterpart of the design. You saw this with methods, where a method's signature defines how its used, but the method implementation can occur anywhere in the class hierarchy. You saw this with abstract classes, where the class' design provides a template for behavior, but that behavior isn't implemented until further down in the hierarchy.

This distinction between the design and the implementation of a class or a method is a crucial part of object-oriented programming theory. Thinking in terms of design when you organize your classes allows you to get the big picture without being bogged down in implementation details. And, having the overall design already defined when you actually start implementing allows you to concentrate on those details, solely on the class you're working on. This programming version of "think globally, act locally" provides a powerful way of thinking about how your classes, your programs, and your overall designs are organized, and how they interrelate with each other.

An interface is made up of a set of method signatures with no implementations, making it the embodiment of pure design. By mixing an interface in with your class, you're encompassing that design into your implementation. Therefore, that design can be safely included anywhere in the class hierarchy because there are no class-specific details of how an interface behaves—nothing to override, nothing to keep track of, just the name and arguments for a method.

What about abstract classes? Don't abstract classes provide this same behavior? Yes and no. abstract classes, and the abstract methods inside them, do provide a separation of design and implementation, allowing you to factor common behavior into an abstract superclass. But abstract classes can, and often do, contain some concrete data (such as instance variables). And, you can have an abstract superclass with both abstract and regular methods, thereby confusing the distinction.

Even a pure abstract class with only abstract method isn't as powerful as an interface. An abstract class is simply another class; it inherits from some other class, and has its place in the hierarchy. abstract classes cannot be shared across different parts of the class hierarchy the way interfaces can. They cannot be mixed into other classes that need their behavior. To attain the sort of flexibility of shared behavior across the class hierarchy, you need an interface.

You can think of the difference between the design and the implementation of any Java class as the difference between the interface hierarchy and the design hierarchy. The singly inherited class hierarchy contains the implementations where the relationships between classes and behavior are rigidly defined. The multiply inherited mixable interface hierarchy, however, contains the design, and can be freely used anywhere it's needed in the implementation. This is a powerful way of thinking about the organization of your program. Although it takes a little getting used to, it's also highly recommended.

Interfaces and Classes


Classes and interfaces, despite their different definitions, have a lot in common. Interfaces, like classes, are declared in source files, one interface to a file. Like classes, they also are compiled using the Java compiler into .class files. And, in most cases, anywhere you can use a class (as a data type for a variable, as the result of a cast, and so on), you can also use an interface.

Almost everywhere that there is a class name in any of this book's examples or discussions, you can substitute an interface name. Java programmers often say class when they actually mean class or interface. Interfaces complement and extend the power of classes, and the two can be treated almost exactly the same. One of the few differences between them is that an interface cannot be instantiated: new can create only an instance of a class.

Implementing and Using Interfaces


Now that you've grasped what interfaces are and why they're powerful (the programming in the large part), it's time to move on to actual bits of code (programming the small). There are two things you can do with interfaces: Use them in your own classes and define your own. First start with the former.

The implements Keyword


To use an interface, you include the implements keyword as part of your class definition. For example, to use an interface called myInterface with a class called myApplet you would use the following definition:




public class myApplet extends java.applet.Applet // java.applet.Applet is the superclass



    implements myInterface {                   // but it also has myInterface behavior



...



}

Because interfaces provide nothing but abstract method definitions, you have to implement those methods in your own classes using the same method signatures from the interface. Note that once you include an interface, you have to implement all the methods in that interface—you can't pick and choose the methods you need. By implementing an interface you're telling users of your class that you support all of that interface. (Note that this is another difference between interfaces and abstract classes—subclasses of the latter can pick which methods to implement or override, and can ignore others.)

Once your class implements an interface, subclasses of your class will inherit those new methods (and can override or overload them) just as if your superclass had actually defined them. If your class inherits from a superclass that implements a given interface, you don't have to include the implements keyword in your own class definition.

Now examine one simple example—creating the new class GoldenRetriever. Suppose you already have a good implementation of the class Animal and an interface, Animallike, that represents what Animals are expected to be able to do. You want a Golden Retriever to be an animal, but you also want it to have doglike characteristics, such as the ability to roll over. Here's how to express it all (don't worry about the definitions of these interfaces for now; you'll learn more about them later today):




interface AnimalLike {



    void  eat();



    void  sleep();



    . . .



}



class  Animal implements AnimalLike {



    private int age;



    private int weight;



    . . .



}



interface  Doglike {



    void  rollOver();



    void  messOnNeighborsLawn();



    . . .



}



class  GoldenRetriever extends Animal implements Doglike {



    . . . // eat()ing may cause me to messOnNeighborsLawn() (A unique behavior)



    . . .



}

Note that the class GoldenRetriever doesn't have to say implements Animallike because, by extending Animal, it already has! One of the nice things about this structure is that you can change your mind about what class GoldenRetriever extends (if a really great Dog class is suddenly implemented, for example), yet class GoldenRetriever will still understand the same two interfaces:




class  Dog implements Doglike {   // extends Object



    private int  ageInDogYears;



    . . .



}



class  GoldenRetriever extends Dog implements Animallike {



    . . .     // users of GoldenRetriever never need know about the change!



}

Implementing Multiple Interfaces


Unlike the singly inherited class hierarchy, you can include as many interfaces as you need in your own classes and your class will implement the combined behavior of all the included interfaces. To include multiple interfaces in a class, just separate the names with commas:




class  GoldenRetriever extends Animal



     implements Doglike, Petlike {



. . .



}

Note that complications might arise from implementing multiple interfaces; what happens if two different interfaces both define the same method? There are three combinations of how this can be solved:


Other Uses of Interfaces


Remember that almost everywhere that you can use a class, you can use an interface instead. For example, you can declare a variable to be of an interface type like the following:




Runnable aRunnableObject = new MyAnimationClass()

When a variable is declared to be of an interface type, it simply means that any object the variable refers to is expected to have implemented that interface—that is, it is expected to understand all the methods that interface specifies. It assumes that a promise made between the designer of the interface and its eventual implementors has been kept. In this case, because ARunnableObject contains an object of the type Runnable, the assumption is that you can call aRunnableObject.run().

The important thing to realize here is that although aRunnableObject is expected to be able to have the run() method, you could write this code long before any classes that qualify are actually implemented (or even created!). In traditional object-oriented programming, you are forced to create a class with stub implementations to get the same effect.

You can also cast objects to an interface, just as you can cast objects to other classes. So, for example, go back to that definition of the GoldenRetriever class, which implemented both the Animallike interface (through its superclass, Animal) and the Doglike interface. Here, you'll cast instances of GoldenRetriever to both classes and interfaces:




GoldenRetriever   aGoldenRetriever    = new GoldenRetriever();



Animal            anAnimal      = (Animal)aGoldenRetriever;



Animallike        anAnimallike  = (Animallike)aGoldenRetriever;



Doglike           aDoglike = (Doglike)aGoldenRetriever;



anAnimal.eat();           // Animals eat



anAnimallike.sleep();     //  and sleep



aAnimallike.rollOver();   // things that are Animallike do not roll over



aDoglike.rollOver();      // but things that are Doglike do



aGoldenRetriever.eat();        // A Golden Retriever can do it all



aGoldenRetriever.sleep();



aGoldenRetriever.rollOver();



aGoldenRetriever.messOnNeighborsLawn();

Declarations and casts are used in this example to restrict a Golden Retriever's behavior to act like a mere animal or dog, but not both. This is shown by the objects anAnimal, anAnimallike, and aDoglike.

Finally, note that, although interfaces are usually used to mix in behavior to other classes (method signatures), interfaces can also be used to mix in generally useful constants. So, for example, if an interface defined a set of constants, and then multiple classes used those constants, the values of those constants could be globally changed without having to modify multiple classes. This is yet another example where the use of interfaces to separate design from implementation can make your code more general and more easily maintainable.

Creating Interfaces


After using interfaces for a while, the next step is to define your own interfaces. Interfaces look a lot like classes; they are declared in much of the same ways, and can be arranged into a hierarchy. However, there are rules for declaring interfaces that must be followed.

New Interfaces


To create a new interface, you declare it like the following:




public interface Growable {



...



}

This is, effectively, the same as a class definition, with the word interface replacing the word class. Inside the interface definition you have methods and constants. The method definitions inside the interface are public and abstract methods; you can either declare them explicitly as such, or they will be turned into public abstract methods if you do not include those modifiers. You cannot declare a method inside an interface to be either private or protected.




public interface Growable {



    public abstract void growIt(); //explicitly public and abstract



    void growItBigger();          // effectively public and abstract



}

Note that, as with abstract methods in classes, methods inside interfaces do not have bodies. Remember, an interface is pure design; there is no implementation involved.

In addition to methods, interfaces can also have variables, but those variables must be declared public, static, and final (making them constant). As with methods, you can explicitly define a variable to be public, static, and final, or it will be implicitly defined as such if you don't use the following modifiers:




public interface Growable {



    public static final int increment = 10;



    long maxnum = 1000000;  // becomes public static and final



    public abstract void growIt(); //explicitly public and abstract



    void growItBigger();          // effectively public and abstract



}

Interfaces must have either public or package protection, just like classes. Note, however, that interfaces without the public modifier do not automatically convert their methods to public and abstract or their constants to public. A non-public interface also has non-public methods and constants that can only be used by classes and other interfaces in the same package.

Interfaces, like classes, can belong to a package by adding a package definition to the first line. Interfaces can also import other interfaces and classes from other packages, just as classes can.

Methods Inside Interfaces


Because methods inside interfaces are supposed to be abstract and apply to any kind of class, how can you define parameters for them? You don't know what class will be using them!

The answer lies in the fact that you can use an interface name anywhere a class name can be used, as you learned earlier today. By defining your method parameters to be interface types, you can create generic parameters that apply to any class that might use this interface.

For example, take the interface Animallike, which defines methods (with no arguments) for eat() and sleep(). There might also be a method for reproduce(), which has one argument: the animal itself. What type is that argument going to be? It can't be Animal because there might be a class that's Animallike (uses the interface) without actually being an animal. The solution is to declare the argument as Animallike in the following interface:




public interface Animallike {



    public abstract reproduce(Animallike self) {



       ...



    }



}

Then, in an actual implementation for this method in a class, you can take the generic Animallike argument and cast it to the appropriate object:




public class GoldenRetriever extends Animal {



    public reproduce(Animallike self) {



       GoldenRetriever aGoldenRetriever = (GoldenRetriever)self;



       ...



    }



}

Extending Interfaces


As with classes, interfaces can be organized into a hierarchy. When one interface inherits from another interface, that "subinterface" acquires all the method definitions and constants that its "superinterface" defined. To extend an interface, you use the extends keyword like you do in a class definition:




public interface Doglike extends Petlike {



...



}

Note that unlike classes, the interface hierarchy has no equivalent of the Object class; the interface hierarchy is not rooted at any one point. Interfaces can either exist entirely on their own or inherit from another interface.

Note also that unlike the class hierarchy, the inheritance hierarchy is multiply inherited. So, for example, a single interface can extend as many classes as it needs to (separated by commas in the extends part of the definition), and the new interface will contain a combination of all its parent's methods and constants.




public interface Doglike extends Petlike, Friendlike {



...}

In multiply inherited interfaces, the rules for managing method name conflicts are the same as for classes that use multiple interfaces; methods that differ only in return type will result in a compiler error.

Exception Handling


The last topic that covered today is exception handling. Exception handling is the capability for your program to respond in a pre-determined way to runtime errors and exceptions. The exception handling capabilities of Java are provided by the statements try, catch, throw, and finally. The basic exception handling structure is the try. . .catch series of statements.

The try. . .catch Structure


The try. . .catch structure is best explained by first examining a simple example. Consider the following code:




public static double Divider (double numerator, double denominator) {



    try{



        double result = numerator / denominator;



        return result;



    }



    catch (ArithmeticException a) {



        System.out.print("An arithmetic exception occurred!");



        return 0;



    }



}

In this example, the method Divider will try the code inside of the try block. If result evaluates okay and the return statement executes without any problems, the method completes and control returns to the calling code. If, however, there is an exception inside this block, control will immediately jump to the catch statement. In this example, if the exception is of type ArithmeticException, a message will be displayed indicating that an arithmetic exception has occurred and 0 will be returned. For example, if a divide by zero is attempted, an ArithmeticException will occur and the message will be displayed.

If additional types of exceptions are possible then additional catch statements can be added. The only requirement is that each catch statement must have a unique type. When an exception occurs, the catch block with the associated type will be executed.

When an exception occurs it is said to be thrown. All exceptions are derived from the class Throwable.

If you list parent classes before subclasses, you can catch errors that did not match any of the types of the subclasses. For example, if you introduce a catch of type Throwable, all errors not caught in previous catch statements will be caught because Throwable is the parent class of all exceptions. The example is expanded in the following code with a catch for Throwable at the end to catch all uncaught exceptions:




public static double Divider (double numerator, double denominator) {



    try{



        double result = numerator / denominator;



        return result;



    }



    catch (ArithmeticException a) {



        System.out.print("An arithmetic exception occurred!");



        return 0;



    }



    catch (Throwable t) {



        System.out.print("An unknown error has occurred!");



        return 0;



    }



}

The finally Statement


Sometimes there is something that must be performed, if an exception occurs or not. Usually, this is to free some external resource after acquiring it, to close a file after opening it, or something similar. To be sure that these no-matter-what statements occur, even if there is an exception, you can use the finally statement.



The primary difference between exception handling in C++ and Java is the finally statement. The finally statement is not supported by C++.

The following example shows the use of the finally statement to make sure that an open file is closed. In this example, the statement f.close() will be executed, whether or not an exception occurs:




aFileClass f = new aFileClass();



try {



    f.open(aPathString)) {



    // additional file operations



    }



    catch (aFileOpenError x) {



        System.out.print("Error opening file");



    }



    catch (aFileWriteError x) {



        System.out.print("Error writing to file");



    }



    // additional catch statements as necessary



    catch (throwable x) {



        System.out.print("An unknown error has occurred");



    }



    finally {



        f.close();



    }



}

In the preceding example, if there are any exceptions or errors in the try block, the appropriate catch block will be executed. After the exception is handled, the finally block will be executed, ensuring that the file is closed.

If there are not any exceptions during the try block, the finally block will still execute and the file will be closed. The finally block always executes, if an exception has occurred or not.

Using throw to Create an Exception


The throw statement is used to create an exception. The following example demonstrates the use of throw (note that the throw and the handler are generally not this close together):




System.out.print("Now ");



try {



    System.out.print("is ");



    throw new MyFirstException();



    System.out.print("This will not execute!");



}



catch (MyFirstException m) {



    System.out.print("the ");



}



System.out.print("time.\n");

The preceding example will print Now is the time.

Exception handlers can be nested. If, for example, a catch block contains a throw statement, the exception will be propagated back up the nested handlers to the next level. If there is not a higher level handler, the system will handle the exception, generally by aborting the program.

Summary


Today, you learned how packages can be used to collect and categorize classes into meaningful groups. Packages are arranged in a hierarchy, which not only better organize your programs, but allow you and the millions of Java programmers out on the Net to name and share their projects uniquely with one another.

You also learned how to use packages, both your own and the many preexisting ones in the Java class library.

You then discovered how to declare and use interfaces, a powerful mechanism for extending the traditional single-inheritance of Java's classes and for separating design inheritance from implementation inheritance in your programs. Interfaces are often used to call common (shared) methods when the exact class involved is not known.

Finally, exception handling was introduced. Through the use of the try. . .catch statement, you can add code to your Visual J++ projects to handle exception conditions, instead of relying on the built-in exception handling of the system, which generally results in aborting the program.

Q&A


Q: Can you say import some.package.B* to import all the classes in that package that begin with B?

A: No, the import asterisk (*) does not act like a command-line asterisk.

Q: Then what exactly does import-ing with an * mean?

A: Combining everything already mentioned in the chapter, the following precise definition emerges: it imports all the public classes that are directly inside the package named, and not inside one of its subpackages. (You can only import exactly this set of classes, or exactly one explicitly named class, from a given package.) By the way, Java only loads the information for a class when you actually refer to that class in your code, so the * form of import is no less efficient than naming each class individually.

Q: Why is full multiple-inheritance so complex that Java abandoned it?

A: It's not so much that it is too complex, but that it makes the language overly complicated and this can cause larger systems to be less trustworthy and, therefore, less secure. For example, if you were to inherit from two different parents, each having an instance variable with the same name, you would be forced to allow the conflict and explain how the exact same references to that variable name in each of your superclasses, and in you (all three), are now different. Instead of being able to call super methods to get more abstract behavior accomplished, you would always need to worry about which of the (possibly many) identical methods you actually wished to call in which parent. Java's runtime method dispatching would have to be more complex as well. Finally, because so many people would be providing classes for reuse on the Net, the normally manageable conflicts that would arise in your own program would be confounded by millions of users mixing and matching these fully multi-inherited classes at will. In the future, if all these issues are resolved, more powerful inheritance might be added to Java, but its current capabilities are already sufficient for 99 percent of your programs.

Q: abstract classes don't have to implement all the methods in an interface themselves, but don't all their subclasses have to?

A: Actually, no. Because of inheritance, the precise rule is that an implementation must be provided by some class for each method, but it doesn't have to be your class. This is analogous to when you are the subclass of a class that implements an interface for you. Whatever the abstract class doesn't implement, the first non-abstract class below it must implement. Then, any further subclasses need do nothing further.

Q: You didn't mention callbacks. Aren't they an important use of interfaces?

A: Yes, but I didn't mention them because a good example would be too bulky in the text. Callbacks are often used in user interfaces (such as window systems) to specify what set of methods are going to be sent whenever the user does a certain set of things (such as clicking the mouse somewhere, typing, and so forth). Because the user interface classes should not know anything about the classes using them, an interface's capability to specify a set of methods separate from the class tree is crucial in this case. Callbacks using interfaces are not as general as using, for example, the perform: method of Smalltalk because a given object can only request that a user interface object call it back using a single method name. Suppose that object wanted two user interface objects of the same class to call it back, using different names to tell them apart? It cannot do this in Java, and it is forced to use special state and tests to tell them apart. (I warned you that it was complicated!) So, although interfaces are quite valuable in this case, they are not the ideal callback facility. Facilities for callbacks are one of the more requested features in Java, and will perhaps be added to the language at a later time.

Previous Page Page Top TOC Next Page