Implementing an Interface

   

Now that you know how to create interfaces, look at how they are used in developing classes. Look again at the JavaBook class that implements our Sellable interface:

 public class JavaBook implements Sellable {   public String getDescription() {     return "Java Book";   }   public String getUnits() {     return "Each";   }   public double getPricePerUnit() {     return 39.95;   }   public double getWeight() {     return 4.5;   } } 

This class satisfies the contract of the interface by providing an implementation for each method. As with classes, implementing abstract methods from an interface is referred to as overriding the methods . The JavaBook class is not restricted from defining methods other than those in the Sellable interface, but it must at least implement those to avoid being declared abstract. If a class has methods other than those in an interface it implements, they are unaffected by the interface. An advantage of interfaces is that they typically can be associated with a class by adding methods that have no effect on the other behavior already required for the class.

Note

Overriding the methods defined by an interface is not enough to implement the interface. When you intend for a class to implement an interface, you must include an implements clause in the class declaration that names the interface. This enables the compiler to verify that the required method overloads are present in the class definition. If you attempt to use an object whose class is not explicitly declared to implement a particular interface as an implementation of that interface, a compiler error is reported , or a ClassCastException results at runtime. This is true even if the class properly overrides every method of the interface.


Overriding the Methods

A class implements an interface by overriding its method declarations with concrete implementations . Remember that if you want to fully implement an interface, you are required to override every method declared in the interface or in any of its super-interfaces. Failure to do so will require you to declare your class abstract.

It might sound bad to end up with an abstract class when working with interfaces, but an abstract interface implementation is actually a good design approach in many cases. It is more common than you might initially think to create an abstract class that provides default behavior for only a portion of an interface's methods. You can then extend this class when you want to create a specific implementation for the behavior that remains. For example, this approach is used widely in the Swing classes in the Java API. Many of the abstract classes found there are partial implementations of interfaces.

Modifiers

As discussed in Chapter 7, you can change the access specifier in a method override only if you change it to one that makes the method more accessible. For example, a protected method can be overridden as public. This simplifies your choices when you implement an interface method because there is nothing more accessible than public, which is the access specifier given to all interface methods. Consequently, all implementations of interface methods must be assigned the public access modifier. Remember that in the class implementation, this modifier must be explicitly assigned because, unlike in an interface declaration, methods in a class do not default to public. This is one reason to go ahead and include the public modifier in the interface declaration. If you do, the method signatures in the interface and the implementing classes will look the same.

Of the remaining modifiers that can be applied to methods, only native and abstract can be applied to methods originally declared in interfaces. You might wonder why abstract is allowed given that an abstract override of a method is the same as not overriding it at all. The advantage is in code readability; declaring an abstract override for a method you do not implement makes it clear to programmers why the resulting class is abstract.

Parameter List

Interface method declarations optionally define a set of parameters that must be passed to the method when it is called. Any attempt to implement a method without declaring the same parameter list results in an overloaded version of the method (at best). A class that implements an interface must declare each overridden method using a parameter list that references the same data types in the same order as the interface declaration. There is nothing wrong with including overloaded methods in your implementation if you need them, but declaring the correct overrides must be your first priority.

Body

The whole point in implementing an interface is to provide a concrete set of behavior that satisfies the contract defined by the interface. As with any class, behavior is defined through method body implementations. Unless you decide to make a method native, it is necessary to create the body for every method originally declared in your interface if you do not want to make your new class abstract.

The actual implementation code in the body of your new method is entirely up to you. This is one of the benefits of using interfaces. Although the interface ensures that ”in a non-abstract class ”its methods will be defined and will return an appropriate data type, the interface places no further restrictions or limitations on the method bodies.

Exceptions

For a method to throw a checked exception, the exception type (or one of its superclasses) must be listed in the exception list in the method declaration. This is no different for exceptions thrown from interface methods. Here are the rules for overriding methods that throw exceptions:

  • The exception list in a method override can only contain exceptions listed in the original exception list, or subclasses of the originally listed exceptions.

  • The exception list in a method override does not need to contain any exceptions, regardless of the number listed in the original exception list. This is because the original list is implicitly assigned to the method override.

  • The method override can throw any exception listed in the original exception list or derived from an exception in the original list, regardless of its own exception list.

In general, the exception list of the method as declared in the interface determines which exceptions can and cannot be thrown. In other words, when a method override changes the exception list, it cannot add any exceptions that are not included in the original interface declaration.

Examine the interface and method declarations in Listing 9.6. These code fragments show both legal and illegal attempts to change the exception list in a method override.

Listing 9.6 Alternate Exception Lists
 interface Example {   public int getPrice(int id) throws java.lang.RuntimeException; } class User implements Example {   public int getPrice(int id) throws java.awt.AWTException {     // Illegal -     // java.awt.AWTException is not a subclass of     //java.lang.RuntimeException   }   public int getPrice(int id) {     if (id == 6) {       throw new java.lang.IndexOutOfBoundsException();       // Legal -       // IndexOutOfBoundsException is derived from RuntimeException     }   }   public int getPrice(int id) throws java.lang.IndexOutOfBoundsException {     // Legal -     // IndexOutOfBoundsException is derived from     //RuntimeException     if (id == 6) {       throw new java.lang.ArrayIndexOutOfBoundsException();         // Legal -         // ArrayIndexOutOfBoundsException is derived from         //     IndexOutOfBoundsException     }   } } 

Interface Method Collisions

One of the strengths of interfaces is the flexibility you get from declaring a class to implement multiple interfaces. Taking advantage of common behavior without restricting yourself to a rigid class hierarchy reduces redundancy in your code without sacrificing implementation independence. However, you must exercise some caution when you design a system based on multiple interface implementation. Look at the following two interface declarations for an example of a problem that might arise when multiple interfaces are imple mented:

 public interface IOne {   /**    * Perform my operation    * @return an integer result    * @throws IOException if the operation fails    */   public int myMethod() throws java.io.IOException; } public interface ITwo {   /**    * Perform my operation    * @return a floating point result    * @throws ParseException if the operation fails    */   public float myMethod() throws java.text.ParseException; } 

The interfaces IOne and ITwo each declare a single method that accepts no parameters. You can declare a class to implement either of these interfaces by providing a corresponding override of myMethod. This works without any problem until you need to declare a class that implements the behavior declared by both IOne and ITwo. Notice that the two declarations of myMethod differ only in return type and the thrown exceptions. It is impossible for a class to implement both interfaces because a class cannot declare two methods with the same name and parameter list even if their return types or thrown exceptions are different. A collision of method declarations between the two interfaces has occurred.

Both the method return type and the exception list cause problems in this example, but the return type difference is the more severe. There is nothing you can do in a class declaration to resolve a discrepancy in return type when a method collision occurs. A difference in exception lists has an impact, but you might be able to work around it. Suppose that ITwo were instead declared as

 public interface ITwo {   /**    * Perform my operation    * @return an integer result    * @throws ParseException if the operation fails    */   public int myMethod() throws java.text.ParseException; } 

With the return types now the same, you can implement both interfaces in a class if the thrown exceptions satisfy the requirements for overriding both versions of the method declaration. The way to do that in this case is to handle any exceptions in the implementation and not declare any as thrown. The following class declaration shows a valid implementation that uses this approach:

 public class OneTwo implements IOne, ITwo {   public int myMethod() {     // do some processing and return the result     return 0;   } } 

You can now use class OneTwo as either interface type, but you have given up the ability to throw exceptions from your implementation of myMethod. If the exception lists in IOne and ITwo were the same, the method collision would never have occurred and a single implementation of the method in a class would satisfy the requirements of both interfaces.

When programmers exercise due caution, method collisions between interfaces are fortunately rare. You will not encounter them when using the Java API unless you declare an interface that reuses a method name already found there. The main guideline related to method collisions is to strive to avoid them. If you select appropriate method names for your interface declarations that precisely describe their purpose, collisions are unlikely .

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net