image

CHAPTER

6

Static Modifier and Interfaces

imagen the previous chapters, you studied several important aspects of object-oriented programming. You learned to declare and use a class, extend its functionality with the help of inheritance, and so on. Java allows multiple levels in an inheritance hierarchy. You learned how the superclass objects are constructed when a subclass object is created. You also learned the use of the super keyword in calling the superclass methods and using superclass fields in subclasses. The access to the various members of a class is controlled using the access modifiers. You learned about all such available access modifiers that control the visibility of members. To restrict inheritance, you use the final keyword. You learned how to create final variables, methods, and classes.

In this chapter, you learn some more stuff related to classes and another important construct—interfaces.

In particular, you will learn the following:

image  Understanding static fields, methods, and initializers
image  Defining and extending interfaces
image  Implementing multiple interfaces
image  Abstract classes

The static Keyword

You have already seen the use of the static keyword in our Java programs. In fact, the first program you encountered in Chapter 2 uses the static keyword, where it was applied to the main method. The static keyword can also be applied to the fields of a class. The only other application of the static keyword is to create initializers. This chapter covers the use of the static keyword in all three cases—that is, used with fields, methods, and initializers—and their importance in practical situations. We’ll start by discussing fields.

The Static Fields

A class field may be marked as static. But why would you do so? Suppose you have created an application that uses several of your own classes. The application user can create several objects of these classes during runtime. Therefore, you may want to know how many objects of each type a user has created in a typical session to get an understanding of the memory usage of your application. In this case, a static field will help in maintaining the count of number of objects created.

Another good application of a static field would be to create a truly global variable. As you already know, Java does not allow you to create any declarations outside the class definition (C++ allows you to create global variable declarations). However, you are able to create a globally accessible variable within your application using the static keyword. One more useful application of a static field declaration is to create an application-level programming constant. You will learn all these techniques in this section.

A static field is associated with the class definition itself. It is shared among all the instances of the class. This means that when a class is instantiated, no separate memory allocation will occur for a static field. The memory allocation for a static field happens only once, and that is when the class loads. When JVM loads the class definition into memory, it allocates space for all the static fields of a class. This memory space (that is, the fields) will be shared among all the objects of a class that are created throughout the life of the program. Conversely, all nonstatic fields will have their own memory allocation in each instance of the class. Therefore, when you have more than one instance of a class, each object will have an independent memory allocation for all its nonstatic fields. In other words, all nonstatic variables are copied into the newly created instance in memory.

Thus, in the case of a nonstatic variable, if you modify its value for a particular instance, the changes will remain local to that object and are not reflected in other instances of the class. However, for a static field, because only one allocation is shared among all the instances, any changes to it will be visible to all the instances of the class. An important application of this would be when you want to keep a count on how many instances of a class have been created by the running application. Let’s look at this usage with the help of an application.

Consider a game that allows a player to create multiple balls during game play. Such an application would probably declare a class called Ball. As a game strategy, the application might keep track of the number of balls created by the player during the entire span of the game. Therefore, the static field would come in handy for counting the number of ball instances. This is illustrated in the program shown in Listing 6-1.

image

Listing 6-1   Program Illustrating the Use of a Static Field

image

The class Ball declares one static attribute called count. The initial value of this attribute is 0:

image

image

The class constructor increments the count value. Recall that each static field gets its own memory allocation, which is shared among all the instances of a class. Therefore, when the class constructor increments the count field, its shared value is incremented and thus it tracks every instantiation of a class. This is shown in Figure 6-1.

image

FIGURE 6-1.   Memory allocation for static and nonstatic fields

The BallGame class provides the main method in which you instantiate the Ball class. To simulate the situation that a player may create any number of balls, we use a randomizer in the program to set the number of balls. The randomizer generates a random number in the range 0 to 10. The program runs 10,000 iterations, creating a random number in each. When the generated random number equals 5, a Ball is created. At the end of the loop, we print the total number of balls created on the user console. The number of balls is retrieved using a getter method on the count field. Note that the getter method getCount is declared static and is called by using the syntax ClassName.methodName.

Accessing Static Fields Through Object References

In the previous section, we accessed the static method getCount by using the class name. Can we call this method on an object reference? Yes, we can. To understand how this is done and what it means, look at the following statement:

image

image

Here, we first create an instance of Ball. The class constructor would increment the count by 1. After the object is fully constructed, the runtime calls its getCount method. Thus, the count value printed to the console also includes the currently created object.

Inheriting Static Fields

In Chapter 4, you learned about inheritance. The same way the class fields and methods are inherited by a subclass, the static fields of a class will be inherited in a subclass. Consider the following class declaration:

image

image

Here, RedBall simply inherits the Ball class, thereby inheriting all its members. Now, if we instantiate RedBall and get the ball count by using the following statement, we find that the count variable belonging to the Ball class is incremented during the construction of the RedBall object:

image

image

This confirms that all static fields are also inherited by the subclasses.

Creating a Truly Global Variable

According to the best practices of object-oriented design, the fields of a class should always be made private. Now, what would happen if we declare them public? Well, the answer is simple. Any code outside the class definition would be able to access and modify these fields. Although this is considered bad design, making the static fields public allows us to create a truly global variable. For example, modify the declaration of the count field in our earlier program as follows:

image

image

Now, we are able to access this count field anywhere in the code with the statement Ball.count. There is no need for a getter method.

image

CAUTION

According to good programming practices recommended by Sun (now Oracle), instance variables should always be declared private. The problem with public non-final variables is that they can be changed by anyone who uses the enclosing class without you knowing. Using getters and setters gives you a chance to monitor/verify any reads or writes of that data, thus leading to more reliable programs. As you’ll recall from the wallet example in Chapter 3, we do not let the cashier take the money out of our wallet; rather, we give it to him. We do this because we always want the chance to check for ourselves that the correct amount is taken out.

image

TIP

It is okay to declare class variables public when they are used as constants and marked final.

Creating Application Constants

From the previous section, you know how to create an application global variable declaration. You also learned the use of the final keyword in Chapter 4. If you make your global variable declaration final, it will create a constant that cannot be modified throughout the application. The following statement shows how to create such a constant:

image

image

The variable PI, which is declared static, is accessible without creating an instance of the Constants class. The variable is initialized to a value of 3.14 at the time of declaration. Because it is declared final, its value cannot be modified further in the application. Thus, you have successfully created an application constant. To use this constant in your code, you would use the syntax Constants.PI. The visibility of this constant in your program code is decided by the access modifier applied to its declaration, which in this case is public. This constant can be accessed anywhere in your program code as long as the code has access to the class Constants.

If you read the web chapter “Syntax Reference 1: Java Language Constructs,” you learned about the coding convention for declaring constants. To reiterate, the names of variables declared as class constants should be all uppercase, with words separated by an underscore character. You should avoid the use of ANSI constants in your program code as your own constants for ease of debugging.

Some Important Notes on Static Fields

Here’s a summary of the observations made so far on static fields:

image  A static field belongs to a class and not to an object (that is, an instance of a class).

image

TIP

For this reason, a static field is also called a class field/attribute or simply a class variable. The nonstatic fields belong to an instance of a class and are therefore referred to as instance variables.

image  Static variables are initialized only once, at the start of the execution.
image  All static fields are initialized before the initialization of any instance variables.
image  A static field can be accessed directly via its class name and doesn’t need any object reference.

The Static Methods

In general, methods may be classified under two categories: the ones whose behavior depends on the state of the object on which they are defined, and the ones that do not depend on the state of the object. Consider the example of the ball game described in the previous section. A player may be given the choice to select the color of the balls. This color choice will be applied to all the balls he creates during the game. Thus, if we define a setDefaultColor method for setting the color of a ball, the method may be attached to the Ball class rather than every object of the Ball type. Such a method is defined using the static keyword, as will be explained further. Now, consider a method that sets the velocity of the motion for a ball. The velocity will obviously depend on the current state of the game and will be independently set for each ball on the board game. Therefore, we’ll want to define a method called setVelocity that operates on an instance of a Ball class. This type of method is a nonstatic method—a method that does not use the static keyword. We discuss both types of methods in this section. First, though, let’s look at a few more situations where the methods need to be declared with the static keyword.

Sometimes you may want to execute program code without creating a class instance. A typical example of this is the declaration of the main method in your program code, as shown here:

image

image

Because the program execution begins with the main method, you need to run it before you can create an instance of your application class. Therefore, the method is declared using the static keyword.

Another example is the use of mathematical functions. To compute an exponential or a logarithm, you would want to call the appropriate function directly instead of creating a class instance and then invoking a method on that instance. For example, Java libraries define a class called Math in the java.lang package. The Math class has several mathematical functions defined in terms of the class methods. All these methods are declared static so that they can be invoked without instantiating the Math class. For example, to determine a logarithm of a given number, you would use the method log, which is invoked as follows:

image

image

This statement would print the log of 5.0 on your terminal. To determine the square root of a given number, you would use the following:

image

image

Both the log and sqrt methods of the Math class are static and can therefore be invoked without an instance of the Math class being created.

Now that we have discussed the need for static methods, let’s go back to our ball game example. Listing 6-2 is an enhanced ball game that creates a random number of balls on each run. Out of the total number of balls created, a randomly selected number of them will be red and the rest green. Each ball has a constant radius, which is also set randomly on each run. As you can imagine, the radius is an appropriate candidate for creating a class field (static) with corresponding getter/setter static methods. Because the color of each ball is randomly set, it becomes the instance property (nonstatic). Finally, we set the velocity of motion for each ball after it is created. Setting the velocity of the ball will be an instance method (nonstatic) because it operates on an individual object. Examine the code in Listing 6-2 to understand the implementation of both static and nonstatic fields and methods.

image

Listing 6-2   Modified Ball Game Program

image
 
image
 
image

The Ball class declares several fields—count, redBallCount, greenBallCount, radius, and defaultColor. The first four are static and the last one is nonstatic. The defaultColor field holds the color value of each individual ball and is therefore set to be an instance variable. The count field, as in the earlier example, tracks each ball that’s created and at the end holds the total number of balls. The redBallCount and greenBallCount fields hold the total number of red and green balls created, respectively. The radius field is common to all balls and is therefore declared static. The class defines the desired getter/setter methods on these fields.

The class constructor takes one parameter of the Color type. Note that Color is a Java-supplied class defined in the java.awt package. The Color class defines several static fields to represent different colors. The constructor increments the static field count to account for the newly created object. The value of the input argument is copied into the defaultColor field. If this input argument is of type Color.RED, we increment the count of red balls; otherwise, we increment the count of green balls. Both these counts are static fields and therefore their values are retained during each instantiation of the Ball class.

Finally, the Ball class declares one nonstatic method called setVelocity that takes one parameter representing the velocity value to be set. This method simply prints the value of the velocity along with the ball number on which the velocity is set to the user’s console. We also print the color value of each ball.

In the main method of the EnhancedBallGame class, we set the number of balls to be created using the randomizer from earlier:

image

image

We set the radius for all the balls to be created by using the randomizer once again:

image

image

Here, setRadius is a static method of the class that sets the value of the static field radius. This value will be common to all instances of the Ball class.

Next, we create the balls by setting up a for loop:

image

image

We select between a red and green ball by once again using the randomizer:

image

image

We use the new keyword to instantiate the Ball class. We call the class constructor by sending either the red or green color value to it. After the class is instantiated, we call the setVelocity nonstatic method on the created instance to set the velocity of the created ball. The velocity value is set again using the randomizer.

After all the balls have been created, the program prints a tally of the red and green balls. A typical output is shown next. Note that this output varies on each run.

image

image

The next section covers the restrictions imposed on the code that can be put in a static method.

Access Restrictions in Static Methods

Because a static method is invoked without a class instance, you cannot use a this or super reference in the static method. That is to say, it is illegal to reference any of the class fields or methods using a this reference within a static method. However, a static method can access the class members (both fields and methods). A class member is a member of the class that is declared using the static keyword. As you have seen so far, both fields and methods can be declared with the static keyword. Such static fields and methods are accessible to the code defined in a static method. The nonstatic fields are bound to a class instance and therefore cannot be accessed in the body of a static method. To understand these code restrictions in a static method, look at the code snippet in Listing 6-3.

image

Listing 6-3   Access Restrictions in Static Methods

image
 
image

The StaticMemberTestApp class declares two fields: The variable field i is declared static whereas the variable j is nonstatic. The main method, which itself is static, modifies the value of i, which is legal. However, accessing j in the body of the main method generates a compile-time error. Note that a static method cannot access a nonstatic attribute within its body. Similarly, the class declares two methods: one static and one nonstatic. The staticMethod is a class method (static) and is called within the body of the main method without producing any compile-time errors. However, calling the nonStaticMethod, which is an instance (nonstatic) method, within the body of the main method generates a compile-time error.

Another important code restriction is that a static method cannot be overridden in a subclass. This is illustrated in the code snippet in Listing 6-4.

image

Listing 6-4   Overriding Methods

image

The class MyClass declares two methods: one static and one nonstatic. The class MySubClass inherits MyClass. The class MySubClass now attempts to override the two methods inherited from its parent. Overriding the staticMethod, which is declared using the static qualifier in the base class, generates a compile-time error. Overriding a nonstatic method (that is, nonStaticMethod) is permitted.

Some Important Notes on Static Methods

Here are a few important points about static methods:

image  A static method is invoked via a class reference. You can use an object reference to invoke a static method; however, this is generally not considered good style.
image  The special method main is declared static so that we do not need to create an instance of the class to which it belongs while starting the application.
image  A static method cannot use this and super in its body.
image  A static method can access static fields and methods of the class.
image  A static method cannot access nonstatic fields and methods of the class.
image  A static method cannot be overridden in a subclass.

The Static Initializers

We use constructors to initialize nonstatic fields of a class. The constructors are called at the time of object creation and complete the initializations defined in them before the object becomes ready to use. You may use the same constructors to initialize static fields of the class. Initializing the static fields in a constructor means that you have to wait until the time of object creation. In certain situations, you will want to initialize the static fields before a class is instantiated. Consider the situation where you have defined a class called GoogleConnector that provides a connection to the Google website. When the user of this class instantiates it, he would naturally expect that the connection is already made and readily available to his code. If connection-making code is written in the class constructor, at times the connection may fail and the created object would not have access to the Google site. Ideally, if we make this connection while loading the class, it will be available to all its created objects. Java allows us to perform these initializations of static fields in what is called a static initializer or simply a static constructor.

A static initializer block resembles a method with no name, no arguments, and no return type. The name is not required because there is no need to refer to it from outside the class definition. Therefore, it is like an anonymous method. Whereas a typical class constructor may contain arguments, a static block cannot have any arguments. Because the static block is executed automatically during the class load time, the arguments to it will not make any sense, and therefore no arguments are allowed. Finally, like a constructor, the static block has no return type.

In our ExtendedBallGame from the previous section, we defined a static field called radius. We initialized it to zero at the time of its declaration. Alternatively, we could have defined a static initializer, as follows, to initialize the static field:

image

image

Now, every ball object will have a default radius of 5 that can be set to another value by calling the setRadius method elsewhere in the object code.

image

NOTE

A static block can access only the static members of the class and does not have access to its nonstatic members because no instance of the class is available when the code defined in the static block executes.

Advantages of a Static Initializer

Although the example in the preceding section illustrates a simple case where a static block is used for initializing a single static field, which could be done by other ways, too, its real purpose lies in the initialization and allocation of resources that are required throughout the life of the class. Here are a few examples:

image  A Modem class may perform initializations of its registers in a static block.
image  A device driver may register the required natives in a static block.
image  In some situations, initialization of a static field may require some prior computations. For example, a digital key signature algorithm may require a random-seed value based on the current time. This would require reading the current time and performing some computations before the seed is generated. Such code may be executed in a static block so that when the class is loaded, the seed for the key generator is readily available.
image  Sometimes you may want to audit whether anybody has loaded a class that has access to sensitive information, as is typical in banking and military applications. A static block can be used for logging such activities.
image  For a typical database application, you may generate prepared SQL statements in a static block.
image  In singletons, you may declare private static members that are initialized in a static block only once. (The singleton is a design pattern that implements the mathematical concept of a singleton by restricting the instantiation of a class to one object.)

Multiple Static Blocks

A static block is executed only once when the class is loaded. Your class may contain multiple static blocks. These are executed in the order in which they appear in the class. This can be useful in organizing the multiple logical initializations your class may require in a sequential order. Consider a class that creates a Sudoku puzzle. Creating a puzzle requires certain steps to be executed in sequence. The class definition to implement this may look like the following:

image

image

The three static blocks execute in the sequence they are specified. Thus, first the random number generator will be initialized, followed by the formulation of the Sudoku solution board, and lastly the code that generates puzzle will be executed. When all three blocks execute successfully, a puzzle will be ready for the user.

Another example would be the GoogleConnector class we discussed earlier. This class might first initialize a modem, followed by making a Wi-Fi connection and then a network connection to the Google site. These three operations can be arranged in three independent static blocks and called sequentially in the given order in the class definition. Other places where you would use multiple static blocks include loading a database driver, followed by a connection to a database, and loading resource bundles for internationalization in a specific sequence. Be aware that loading expensive resources in a static block may not always be a good idea. What’s more, handling failures in a static block is usually difficult, as explained later in the chapter.

An Alternative to a Static Initializer

Instead of using a static block as discussed in the previous section, you can initialize your static fields by defining a private static method. Let’s look at how to do this in our ExtendedBallGame example. The game has a static field called radius. We could initialize this field using the following code snippet:

image

image

Defining a method like this for initialization is really useful if you have to reinitialize your objects later in the code. You may then simply call this private class method whenever a re-initialization is desired. For example, in our ball game, when two balls touch each other, we may want to create a bigger ball out of the two balls by merging them into a single ball. Thus, the initialization method gives us more flexibility in coding as compared to a static initialization block.

Some Important Notes on Static Initializers

Here are a few important points on using static blocks to keep in mind while coding these initializers:

image  The JVM sets a limit of 64K on the size of a static initializer block. Therefore, you cannot put lot of code in this block.
image  You cannot throw checked exceptions from a static initializer. The exceptions are discussed in Chapter 8 on exception handling.
image  You cannot use the this keyword in a static block because no instance has been created so far.
image  You cannot call super explicitly in a static block. Static blocks are loaded when the class is loaded, and super is called whenever object creation takes place. Therefore, it is built into a nonstatic initializer (that is, a constructor). That’s why including it in a static block results in generating a compile-time error.
image  There is no return type for a static block.
image  Testing the code in a static block is usually considered a nightmare by developers.

image

CAUTION

Although exceptions are discussed in depth in Chapter 8, we will briefly discuss here how to handle exceptions in static blocks. In our earlier programs, the methods catch and handle exceptions using a try-catch block. In static blocks, we cannot use try-catch. Therefore, one option is to log an exception and then throw a RuntimeException to end the current thread. Another option would be to call the System.exit() method. This, however, is not desirable in a managed environment such as a Servlet. This option is typically used in Java applications where the static block performs some critical operation without which the program cannot be run successfully—for example, loading the database driver. A third option would be to set a flag indicating the failure. The class constructor can then check the condition of the flag and throw an exception if desired. Lastly, if the operation in a static block is not really critical, we can just log the exception entry and continue.

Interfaces

As mentioned earlier, Java does not support multiple inheritance—in other words, a class cannot have two or more superclasses. Multiple inheritance has its own advantages and disadvantages. Java achieves some of the benefits of multiple inheritance through interfaces. So what is an interface? An interface is a device or system that unrelated entities use to interact with each other. When you drive a car, you interact with a machine—two totally unrelated entities. These two entities interact through a well-defined interface for steering, throttling, and braking. The English and French languages may be considered an interface for communication between two people— not totally unrelated entities in this case. A remote control is an interface between a viewer and a television set. In the military, the interface between officers of different rank is the enforced protocol of behavior. Java interfaces are analogous to such protocols; they provide an agreed-upon behavior between objects of different types or of the same type.

image

NOTE

Other object-oriented languages also provide the functionality of Java’s interfaces. For example, C++ provides an equivalent interface through its declaration of abstract base classes; Objective-C provides similar functionality through its protocols, and so on.

Interfaces may be considered a standard framework for accessing classes. They provide a separation between behavior and implementation. We will cover this point in more depth when we discuss a concrete, real-life example in the next section.

image

TIP

Object-oriented languages have a concept of composition, where an object of a different type is composed within another object by holding a reference to the other object. Interfaces allow a kind of behavioral composition with the restriction that they do not allow the classes that implement them to inherit implementation from multiple classes. Both composition and inheritance allow you to place other objects inside your new class. Composition does this explicitly whereas inheritance does it implicitly. Composition is used when you want the features of an existing class inside your new class but not its interface. To do this, you embed private objects of existing classes inside your new class.

So what does an interface look like? An interface has a structure similar to a class. It contains methods and fields. However, none of the methods of an interface can contain implementation code, and all fields must be declared final. Therefore, the methods defined in an interface provide only the signatures, and all the fields are program constants. You need to define a class in your program that provides the implementations of the methods declared in an interface. A class can implement multiple interfaces, and an interface can have multiple super-interfaces. You will learn more about this as you continue reading this section.

Although there seems to be a lot of similarity between interfaces and multiple inheritance, there are some subtle important differences:

image  A class can inherit the fields of a superclass, but it inherits only the constants from an interface (note that an interface does not allow field declarations).
image  A class can inherit method implementations from a superclass, but it cannot inherit method implementations from an interface because there are none in an interface.
image  In the case of multiple inheritance, all involved classes in the hierarchy are somehow related to each other; in the case of interfaces, the classes that implement them may or may not be related through the class hierarchy.

Now that you know what an interface is, the next question is where to use it. Therefore, let’s discuss the various uses of an interface:

image  Interfaces are useful in capturing similarities between unrelated classes without the need to force a relationship between them in the class hierarchy. Think of an Employee class and a Stock class—both require a print functionality whereby the user can print its description. Thus, we could create an interface called Printable that has a Print method (besides other methods for the printer, page settings, and so on). Both the Employee and Stock classes will implement this Printable interface to provide a common functionality, but otherwise the classes are unrelated.
image  Interfaces allow you to define behavior that one or more classes are expected to implement. In the Printable interface example, Print, PageSetting, PrinterSetting, and so on, would be the methods of the Printable interface that define the behavior for various classes.
image  Interfaces allow you to hide the implementation details of an object. For example, as discussed earlier, to drive a car, you need not be concerned with how the fuel is ignited in the engine’s cylinders. Many times, you can provide anonymous objects to the user by revealing only the object’s programming interface.

image

NOTE

All methods in an interface are public and abstract by default. A method is said to be “abstract” when no implementation is provided for it.

image

NOTE

Interfaces with no methods are known as marker interfaces. A known example of this is the Serializable interface defined in the Java API. To save an object to a file or to send it across a network connection, the object’s class must implement this interface. The writeObject method in the ObjectOutputStream class of the Java API accepts a parameter of type Object, which is also an instance of Serializable.

A Real-life Example of an Interface

Suppose you are asked to develop communication software such as an FTP (File Transfer Protocol) or Telnet program that uses a modem. Your program must work with a variety of different modems. Although all the modems provide the same functionality, their implementations are quite different. Obviously, it would be inadvisable to create multiple versions of your application to interface with each of the modems available on the market because the code maintenance and application upgrades would be too much work. For this reason, you would develop an interface that specifies the method signatures your application uses for interfacing with the modem. This would provide a uniform programming interface to all the modems. This way, the application that uses a modem would not break even if the implementations in the methods of the modem change in future. Typically, you would have open, close, read, and write operations that your application would invoke on a modem. Your interface would declare these methods as follows:

image

image

image

NOTE

This is an extremely simplified view of the kind of API a modem might present. An interface for a modem in real life would have several more methods.

You will provide different implementations for the methods declared in the Modem interface for each of the supported modems.

To implement an interface, you use the following notation:

image

image

The class HayesModem declaration uses the implements keyword to implement the Modem interface. In the class definition, you need to provide an implementation for each method defined in the Modem interface.

image

NOTE

If you do not implement all the methods of the interface, the class becomes abstract. Abstract classes are discussed later in this chapter.

To support another modem, you would create another class (say, IntelModem). You would define this class as follows:

image

image

The method implementations in this class would be different from the implementations provided in HayesModem class. Each implementation would be specific to the modem manufacturer. Once you create such classes specific to each modem manufacturer, you can develop your application software that interfaces easily with each of these modems. To use the HayesModem class, you would use code similar to the following:

image

image

Note that you instantiate the HayesModem class and assign the object reference to a variable of type Modem. Remember from earlier chapters that an object reference can be assigned to its superclass without explicit typecasting. The Modem interface is a super-interface here. The HayesModem class implements Modem and therefore the assignment of an object reference from type HayesModem to the Modem interface is permitted.

Now, to interface your application with an Intel modem, you would use the following code:

image

image

The only difference between the earlier code and this code is in the class instantiation. In the earlier case we use HayesModem and in the latter case we use IntelModem. The rest of the code remains the same. This is the greatest advantage of creating interfaces—you don’t need to change much of the code even when you change modems. This makes it easier to write an application that works with a lot of different modems. Here, you can see that your initial concern for how to provide a different implementation for each modem and yet maintaining a single interface to access them is easily resolved using an interface.

Understanding Interface Syntax

An interface uses syntax similar to that of a class declaration. The format of the interface syntax is shown here:

image

image

A typical interface declaration looks like the following:

image

image

The interface is defined using the interface keyword. The interface has a name that follows the standard naming conventions for a class declaration. The Modifiers control its visibility, which can be either public or default. Therefore, you either specify this field as public or none. If you use a public modifier, you must put your interface definition in a separate compilation unit (a .java file). As in the case of classes, a public access modifier allows the interface to be accessed outside of the package in which it is declared.

image

NOTE

The Java Language Specifications lists the following values as the allowed values for ModifiersAnnotation, public, protected, private, abstract, static, and strictfp. The protected and private modifiers can be applied only to member interfaces within a directly enclosing class declaration. You learn more about this in Chapter 7.

The extends keyword has a similar meaning as in the case of a class declaration. An interface may extend another interface. When an interface extends another interface, it adds a few more constants and/or method declarations to the existing interface. However, it is not allowed to provide an implementation for any of the new methods or the methods inherited from an existing interface.

image

CAUTION

Because none of the methods in an interface are implemented, the interface itself is considered abstract by default. Because every interface is implicitly abstract, the abstract modifier is obsolete and should not be used in new programs.

In the interface body, you declare constants and method signatures. An interface is allowed to declare constants but not variables. The declaration of a method signature in an interface is also called abstract method declaration. Because these methods do not contain any implementation, they are called abstract.

image

CAUTION

You cannot apply the following modifiers to the interface methods: private, protected, transient, volatile, and synchronized.

Understanding Interfaces Through an Example

To illustrate how to declare and implement an interface, let’s look at a concrete example. Suppose we are asked to represent different kinds of vehicles in our application software. A vehicle could run on gasoline or electric batteries. Therefore, we will have two different classes of vehicles. Each type would have its own fuel-efficiency measure. For gas vehicles, it is the gasoline consumed per mile, and for electric vehicles it is the kilowatts (KW) of power consumed per mile. Because this is common functionality and must be implemented by every type of car, including those that will come on the market in the future, let’s create a standard interface that every vehicle will implement. We’ll call this interface MileageEfficiency. Any vehicle that implements this interface will get a standard set of methods for obtaining the vehicle’s efficiency. To keep things short and simple, we’ll define this interface with a single method, as follows:

image

image

The GasVehicle and ElectricVehicle classes we will be writing shortly will implement this interface and provide an appropriate implementation for its sole method—getMilesPerGallon. The complete program is given in Listing 6-5.

image

Listing 6-5   A Program Illustrating Interfaces

image
 
image

Both the GasVehicle and ElectricVehicle classes define the MileageEfficiency interface and provide their own unique implementation for the method getMilesPerGallon. Both classes also define a method called makeTrip that records the fuel consumed and the distance traveled on a trip. The TestDrive class defines a main function that creates an instance of both the vehicles, makes a trip on each, and prints the fuel efficiency after the trip. When we run the program, we see the following output:

image

image

Note that the MileageEfficiency interface we have created can be applied to any other vehicle type that may come in the future. You will see this when we create a hybrid vehicle in the next section.

Extending Interfaces

It is possible to extend an existing interface. The purpose behind doing so is to add more declarations, both constants and methods, to an existing interface. This helps in not breaking the existing applications that have implemented the earlier interface. We will now extend our MileageEfficiency interface from the previous example to provide a new way to compute the fuel efficiency of the newly introduced hybrid cars on the market that use both gasoline and electric batteries. We define this new interface as follows:

image

image

To extend an existing interface, we use the keyword extends, just the way we did for extending class definitions. The interface declares two new methods: one for computing the fuel efficiency of the car and the other one for the battery consumption. The getMilesPerGallon method of the base interface will have an altogether different implementation that uses these two efficiencies to return a newly computed efficiency to the user. The declaration of the HybridVehicle class that implements this interface and the test program that creates an instance of this hybrid car are given in Listing 6-6.

image

Listing 6-6   Modified Test Drive Program

image
 
image

The class HybridVehicle implements the newly declared interface ExtendedMileageEfficiency and provides the implementation of its two methods, along with the implementation of the inherited getMilesPerGallon method. The main method creates an instance of the hybrid vehicle, makes a trip, and then prints the car’s efficiency.

image

NOTE

The actual computation of this hybrid efficiency would be more complicated than the simplistic calculations made here to illustrate the concept.

By this time, you have certainly started realizing (and appreciating) the use of interfaces. The interface we initially created provided a standard notation to the developer to compute the fuel efficiency of different types of cars. Later on, when the technology has been enhanced and new types of cars are introduced on the market, we could extend our existing interface and yet retain the same interface method getMilesPerGallon to compute the efficiency of the new cars. For a developer, the consistent interface he sees while writing an application is the greatest advantage to using interfaces.

image

NOTE

An interface can extend another interface but it cannot implement any interface.

Implementing Multiple Interfaces

In the previous section, we created a standard interface to get the fuel efficiency of a car. A car has many such standard functions that could be defined in terms of interfaces. For example, we could define interfaces for steering, braking, refilling, and so on. We’ll now create one such interface for tracking the remaining battery life of electric and hybrid vehicles. Obviously, this interface is of no use to a car that runs on gasoline. Therefore, only electric and hybrid cars will implement our new interface. Let’s call this interface BatteryLifeTracker. The interface definition is shown in the following code snippet:

image

image

The BatteryLifeTracker interface defines a constant that specifies the maximum number of times the car battery can be charged. It also defines a standard interface method called chargeBattery that increments the charge counter. The getRemainingLife method returns the number of times the battery can still be charged before it is rendered useless.

Both ElectricVehicle and HybridVehicle classes implement this interface. The class declaration now looks like this:

image

image

Note that the two interface names are separated with a comma. As a matter of fact, we could have any number of interfaces listed here, each separated with a comma. For each interface, we must implement all its methods within the class body.

Both ElectricVehicle and HybridVehicle now declare a static counter called numberOfRecharges to keep record of how many times the battery has been charged:

image

image

The implementations of the chargeBattery method in these classes simply increment this charge count. The implementation of the getRemainingLife method returns the difference between the total charge count and the number of times the battery has been charged so far. The main method prints this useful battery life information for both the cars. The complete program is given in Listing 6-7.

image

Listing 6-7   Implementing Multiple Interfaces

image
 
image
 
image

When we run the program, the following output is produced:

image

image

Combining Interfaces

Java does not allow you to extend a class from more than one class. However, you can create an interface that extends one or more interfaces. For example, we could add a new interface that provides a method for computing the efficiency of a car irrespective of whether it runs on gasoline or a battery. Such an interface is declared as follows:

image

image

Any class that implements EfficiencyCalc has to provide the implementation not only for the getCarEfficiency method but also for all the inherited methods of MileageEfficiency and BatteryLifeTracker. Implementing multiple interfaces like this allows them to inherit the behavioral protocols of the parent interfaces.

A Few Important Points on Interfaces

Here are a few important points you should keep in mind concerning interfaces:

image  An interface is very similar to a class, except that it can have only fields that are implicitly public and static and method declarations that are implicitly public and abstract.
image  The Java API documentation lists interfaces like classes.
image  The interfaces compile to a .class file and get loaded by the same process that loads classes.
image  You can create a reference variable whose type is the interface name. Only the methods defined in an interface will be visible through this reference.
image  Any constants defined by an interface can be accessed without a prefix from code within the class because implementing the interface makes them part of the implementing class.

Abstract Classes

For an interface containing several methods, a developer can provide the implementation for some of the methods in a class that implements the interface. However, the developer may not be in a position to implement all the methods of the interface and might leave that task to a colleague or senior developer to perform at a later time. In such a case, compiling the class would result in compile-time errors. This situation can be remedied by declaring the class as abstract, and you create an abstract class using the abstract keyword.

Let’s consider the case where a developer implementing our HybridVehicle class does not know the implementation of the getMilesPerGallon method, which requires a few computations that may not be known at the time of code development. In such a situation, the developer can provide the implementation of all other methods of the two interfaces, except for the getMilesPerGallon method. The class HybridVehicle must now be declared abstract, as follows:

image

image

The code will now compile; however, the developer will not be able to create an instance of HybridVehicle anywhere in the program. To create an instance of HybridVehicle, he will probably extend this class further and provide the implementation of the getMilesPerGallon method in the new class.

A typical use of abstract classes is seen in our earlier example of the Modem interface. In the Modem interface, we declared four methods: open, close, read, and write. We could add one more method called init to this interface. The purpose of the init method, as the name suggests, is to initialize the modem. The new interface is shown in the following code snippet:

image

image

Because the implementation of the read and write methods is mostly the same for all the modems, we can provide these implementations for the benefit of modem manufacturers. However, the implementation of the open, close, and init methods will differ for each manufacturer. In particular, the init method that initializes the modem hardware will surely vary from manufacturer to manufacturer. Therefore, we may create a new class, AbstractModem, that provides the implementation of the Modem interface except for the implementations of open, close, and init methods. This class must be declared abstract because it does not provide the implementations of all the methods of the implementing interface. The class definition is shown here:

image

image

Thus, the abstract classes allow you to provide the partial implementation of the implementing interface and leave the rest of the implementation to another developer. Because the abstract class has some missing implementation, an abstract class cannot be instantiated. The following statement would generate a compile-time error:

image

image

image

NOTE

An interface is abstract by nature because all its methods are abstract. Some people think of an abstract class as a mixture of a concrete class and an interface. Some people prefer using abstract classes as a way of defining a behavioral protocol for interfaces.

image

NOTE

Abstract classes cannot be instantiated, but they can be subclassed.

Here are some important differences between an interface and an abstract class:

image  An interface contains only the method signatures whereas an abstract class may have some of its methods implemented.
image  All of an interface’s methods are public by default. You cannot apply any other access modifiers to the methods declared in an interface. In an abstract class, the implemented methods can have access modifiers applied to them in their declarations. For this, the methods have to be public in an interface. Declaring them protected or private would result in an error. In an abstract class, you can apply a protected modifier to an implemented method but you cannot make it private.
image  An interface can extend multiple interfaces. An abstract class cannot be extended from more than one abstract class.
image  All methods in an interface are implicitly abstract. An abstract class may have a few concrete methods.
image  An interface does not have a constructor. An abstract class may declare a constructor.

Summary

This chapter covered several important features of class declarations in Java. You saw the use of the static keyword in classes. The static keyword can be applied to methods and fields of a class. A method that is declared static can be invoked without the enclosing class being instantiated. A static field behaves like a program constant.

Java provides interfaces to incorporate the benefits offered by multiple inheritance in other languages. An interface consists of method signatures (with no implementations) and only final variables. A class uses an interface with the help of an implements keyword. The implementing class must provide implementation for all methods of the implemented interface; otherwise, the class becomes abstract. An interface can extend another interface. A class may implement multiple interfaces.

An abstract class implements some of the methods of the interface it inherits. An abstract class cannot be instantiated; however, another class can extend it. You create abstract classes when you do not know the implementation of some of the interface methods at the time of development.

In the next chapter, we define and use an inner class and discuss the many aspects of it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.9.141