11.3. Discovering Class Relationships

 
[Page 347 ( continued )]

10.4. Interfaces

An interface is a classlike construct that contains only constants and abstract methods . In many ways, an interface is similar to an abstract class, but an abstract class can contain variables and concrete methods as well as constants and abstract methods.


[Page 348]

To distinguish an interface from a class, Java uses the following syntax to declare an interface:

 modifier    interface    InterfaceName {  /** Constant declarations */   /** Method signatures */  } 

An interface is treated like a special class in Java. Each interface is compiled into a separate bytecode file, just like a regular class. As with an abstract class, you cannot create an instance from an interface using the new operator, but in most cases you can use an interface more or less the same way you use an abstract class. For example, you can use an interface as a data type for a variable, as the result of casting, and so on.

Suppose you want to design a generic method to find the larger of two objects. The objects can be students, circles, or rectangles. Since compare methods are different for different types of objects, you need to define a generic compare method to determine the order of the two objects. Then you can tailor the method to compare students, circles, or rectangles. For example, you can use student ID as the key for comparing students, radius as the key for comparing circles, and area as the key for comparing rectangles. You can use an interface to define a generic compareTo method, as follows :

  // Interface for comparing objects, defined in java.lang    package   java.lang;    public interface   Comparable  {    public int   compareTo(Object o);  } 

The compareTo method determines the order of this object with the specified object o , and returns a negative integer, zero, or a positive integer if this object is less than, equal to, or greater than the specified object o .

Note

The Comparable interface has been available since JDK 1.2, and is included in the java.lang package.


Many classes in the Java library (e.g., String and Date ) implement Comparable to define a natural order for the objects. If you examine the source code of these classes, you will see the keyword implements used in the classes, as shown below:

Thus strings are comparable, and so are dates. Let s be a String object and d be a Date object. All the following expressions are all true:

A generic max method for finding the larger of two objects can be declared, as shown in (a) or (b):


[Page 349]

The max method in (a) is simpler than the one in (b). In the Max class in (b), o1 is declared as Object , and (Comparable)o1 tells the compiler to cast o1 into Comparable so that the compareTo method can be invoked from o1 . However, no casting is needed in the Max class in (a), since o1 is declared as Comparable .

The max method in (a) is more robust than the one in (b). You must invoke the max method with two comparable objects. Suppose you invoke max with two noncomparable objects

 Max.max(anyObject1, anyObject2); 

The compiler will detect the error using the max method in (a), because anyObject1 is not an instance of Comparable . Using the max method in (b), this line of code will compile fine, but will have a runtime ClassCastException , because anyObject1 is not an instance of Comparable and cannot be cast into Comparable .

From now on, assume that the max method in (a) is in the text. Since strings are comparable and dates are comparable, you can use the max method to find the larger of two instances of String or Date . Here is an example:

The return value from the max method is of the Comparable type. So you need to cast it to String or Date explicitly.

10.4.1. Declaring Classes to Implement Comparable

You cannot use the max method to find the larger of two instances of Rectangle , because Rectangle does not implement Comparable . However, you can declare a new rectangle class that implements Comparable . The instances of this new class are comparable. Let this new class be named ComparableRectangle , as shown in Listing 10.3.

Listing 10.3. ComparableRectangle.java
(This item is displayed on pages 349 - 350 in the print version)
 1   public class   ComparableRectangle   extends   Rectangle 2    implements   Comparable  { 3  /** Construct a ComparableRectangle with specified properties */  4   public   ComparableRectangle(   double   width,   double   height) { 5   super   (width, height); 6 } 7 

[Page 350]
 8  /** Implement the compareTo method defined in Comparable */  9    public int   compareTo(Object o)  { 10   if   (getArea() > ((ComparableRectangle)o).getArea()) 11   return 1   ; 12   else if   (getArea() < ((ComparableRectangle)o).getArea()) 13   return -1   ; 14   else   15   return 0   ; 16 } 17 } 

ComparableRectangle extends Rectangle and implements Comparable , as shown in Figure 10.4. The keyword implements indicates that ComparableRectangle inherits all the constants from the Comparable interface and implements the methods in the interface. The compareTo method compares the areas of two rectangles. An instance of CompareRectangle is also an instance of Rectangle , GeometricObject , Object , and Comparable .

Figure 10.4. ComparableRectangle extends Rectangle and implements Comparable .

You can now use the max method to find the larger of two objects of CompareRectangle . Here is an example:

 ComparableRectangle rectangle1 =   new   ComparableRectangle(   4   ,   5   ); ComparableRectangle rectangle2 =   new   ComparableRectangle(   3   ,   6   ); System.out.println(Max.max(rectangle1, rectangle2)); 

An interface provides another form of generic programming. It would be difficult to use a generic max method to find the maximum of the objects without using an interface in this example, because multiple inheritance would be necessary to inherit Comparable and another class, such as Rectangle , at the same time.

The Object class contains the equals method, which is intended for the subclasses of the Object class to override in order to compare whether the contents of the objects are the same. Suppose that the Object class contains the compareTo method, as defined in the Comparable interface; the new max method can be used to compare a list of any objects. Whether a compareTo method should be included in the Object class is debatable. Since the compareTo method is not defined in the Object class, the Comparable interface is created in Java 2 to enable objects to be compared if they are instances of the Comparable interface. It is strongly recommended (though not required) that compareTo should be consistent with equals . That is, for two objects o1 and o2 , o1.compareTo(o2) == 0 if and only if o1.equals(o2) is true .

10.4.2. Interfaces vs. Abstract Classes

An interface can be used the same way as an abstract class, but declaring an interface is different from declaring an abstract class. Table 10.1 summarizes the differences.


[Page 351]
Table 10.1. Interfaces vs. Abstract Classes
  Variables Constructors Methods
Abstract class No restrictions Constructors are invoked by subclasses through constructor chaining. An abstract class cannot be instantiated using the new operator. No restrictions
Interface All variables must be public static final No constructors. An interface cannot be instantiated using the new operator. All methods must be public abstract instance methods

Note

Since all data fields are public final static and all methods are public abstract in an interface, Java allows these modifiers to be omitted. Therefore the following declarations are equivalent:


Tip

A constant defined in an interface can be accessed using the syntax InterfaceName.CONSTANT_NAME (e.g., T1.K ).


Java allows only single inheritance for class extension, but multiple extensions for interfaces. For example,

   public class   NewClass    extends    BaseClass    implementes    Interface1, ..., InterfaceN { ... } 

An interface can inherit other interfaces using the extends keyword. Such an interface is called a subinterface . For example, NewInterface in the following code is a subinterface of Interface1 , ..., and InterfaceN :

   public      interface    NewInterface    extends    Interface1, ..., InterfaceN {  // constants and abstract methods  } 

A class implementing NewInterface must implement the abstract methods defined in NewInterface , Interface1 , ..., and InterfaceN . An interface can only extend other interfaces, not classes. A class can extend its superclass and implement multiple interfaces.

All classes share a single root, the Object class, but there is no single root for interfaces. Like a class, an interface also defines a type. A variable of an interface type can reference any instance of the class that implements the interface. If a class implements an interface, the interface is like a superclass for the class. You can use an interface as a data type and cast a variable of an interface type to its subclass, and vice versa. For example, suppose that c is an instance of Class2 in Figure 10.5. c is also an instance of Object , Class1 , Interface1 , Interface1_1 , Interface1_2 , Interface2_1 , and Interface2_2 .


[Page 352]
Figure 10.5. Abstract class Class1 implements Interface1, Interface1 extends Interface1_1 and Interface1_2. Class2 extends Class1 and implements Interface2_1 and Interface2_2 .

Caution

On rare occasions, a class may implement two interfaces with conflict information (e.g., two identical constants with different values or two methods with the same signature but different return types). This type of error will be detected by the compiler.


Abstract classes and interfaces can both be used to model common features. How do you decide whether to use an interface or a class? In general, a strong is-a relationship that clearly describes a parent “child relationship should be modeled using classes. For example, a staff member is a person, so the relationship between them should be modeled using class inheritance. A weak is-a relationship , also known as an is- kind-of relationship , indicates that an object possesses a certain property. A weak is-a relationship can be modeled using interfaces. For example, all strings are comparable, so the String class implements the Comparable interface. You can also use interfaces to circumvent the single-inheritance restriction if multiple inheritance is desired. In the case of multiple inheritance, you have to design one as a superclass and others as interfaces. See Chapter 11, "Object-Oriented Design," for more discussion.

Note

Class names are nouns. Interface names may be adjectives or nouns. For example, both java.lang.Comparable and java.awt.event.ActionListener are interfaces. Comparable is an adjective, and ActionListener is a noun. ActionListener will be introduced in Chapter 14, "Event-Driven Programming."


10.4.3. Creating Custom Interfaces

An example of implementing the Comparable interface is given in §10.4.1, "Declaring Classes to Implement Comparable". This section creates a custom interface. Suppose you want to describe whether an object is edible. You can declare the Edible interface:

    public interface   Edible {   /** Describe how to eat */     public   String howToEat();  } 

To denote that an object is edible, the class for the object must implement Edible . Let us create the following two sets of classes:

  • Create a class named Animal and its subclasses Tiger , Chicken , and Elephant . Since chicken is edible, implement the Edible interface for the Chicken class, as follows:

       class   Animal { } 

    [Page 353]
        class   Chicken   extends   Animal   implements   Edible, Comparable  {   int   weight;   public   Chicken(int weight) {   this.   weight = weight; }    public   String howToEat()  {   return "Fry it"   ; }    public   int compareTo(Object o)  {   return   weight “ ((Chicken)o).weight; } }   class   Tiger   extends   Animal { } 

A class may implement several interfaces. The Chicken class also implements the Comparable interface to compare two chickens according to their weight.

  • Create a class named Fruit and its subclasses Apple and Orange . Since all fruits are edible, implement the Edible interface for the Fruit class. The Fruit class is abstract, because you cannot implement the howToEat method without knowing exactly what the fruit is. In the Apple class and the Orange class, implement the howToEat method, as follows:

        abstract class   Fruit   implements   Edible  { }   class   Apple   extends   Fruit {    public   String howToEat()  {   return "Make apple cider"   ; } }   class   Orange   extends   Fruit {    public   String howToEat()  {   return "Make orange juice "   ; } } 

To demonstrate how the Edible interface may be used, create the following program that creates an array with three objects. The showObject method invokes the howToEat() method if the object is edible.

   public class   TestEdible {   public static void   main(String[] args) { Object[] objects = {   new   Tiger(),   new   Chicken(),   new   Apple()};   for   (   int   i =     ; i < objects.length; i++)  showObject(objects[i]);  }    public static void   showObject(Object object)  {   if   (object   instanceof   Edible) System.out.println(((Edible)object).howToEat()); } } 

The program displays

 Fry it Make apple cider 


[Page 354]

10.4.4. (Optional) The Cloneable Interface

An interface contains constants and abstract methods, but the Cloneable interface is a special case. The Cloneable interface in the java.lang package is defined as follows:

   package   java.lang;   public interface   Cloneable { } 

This interface is empty. An interface with an empty body is referred to as a marker interface . A marker interface does not contain constants or methods. It is used to denote that a class possesses certain desirable properties. A class that implements the Cloneable interface is marked cloneable, and its objects can be cloned using the clone() method defined in the Object class.

Many classes in the Java library (e.g., Date , Calendar , and ArrayList ) implement Cloneable . Thus, the instances of these classes can be cloned. For example, the following code

 Calendar calendar =   new   GregorianCalendar(   2003   ,   2   ,   1   ); Calendar calendarCopy = (Calendar)calendar.clone(); System.out.println(   "calendar == calendarCopy is "   + (calendar == calendarCopy)); System.out.println(   "calendar.equals(calendarCopy) is "   + calendar.equals(calendarCopy)); 

displays

 calendar == calendarCopy is false calendar.equals(calendarCopy) is true 

To declare a custom class that implements the Cloneable interface, the class must override the clone() method in the Object class. Listing 10.4 declares a class named House that implements Cloneable and Comparable .

Listing 10.4. House.java
(This item is displayed on pages 354 - 355 in the print version)
 1   public class    House   implements   Cloneable, Comparable  { 2   private int   id; 3   private double   area; 4   private   java.util.Date whenBuilt; 5 6   public   House(   int   id,   double   area) { 7   this   .id = id; 8   this   .area = area; 9 whenBuilt =   new   java.util.Date(); 10 } 11 12   public double   getId() { 13   return   id; 14 } 15 16   public double   getArea() { 17   return   area; 18 } 19 20   public   java.util.Date getWhenBuilt() { 21   return   whenBuilt; 22 } 23 

[Page 355]
 24  /** Override the protected clone method defined in the Object  25  class, and strengthen its accessibility */  26    public   Object clone()  { 27   try   { 28    return super.   clone();  29 } 30   catch   (CloneNotSupportedException ex) { 31   return null   ; 32 } 33 } 34 35  /** Implement the compareTo method defined in Comparable */  36    public int   compareTo(Object o) {  37   if   (area > ((House)o).area) 38   return 1   ; 39   else if   (area < ((House)o).area) 40   return -1   ; 41   else   42   return 0   ; 43 } 44 } 

The House class overrides the clone method (lines 26 “33) defined in the Object class. The clone method in the Object class is defined as follows:

   protected native   Object clone()   throws   CloneNotSupportedException; 

The keyword native indicates that this method is not written in Java, but is implemented in the JVM for the native platform. The keyword protected restricts the method to be accessed in the same package or in a subclass. For this reason, the Cloneable class must override the method and change the visibility modifier to public so that the method can be used in any package. Since the clone method implemented for the native platform in the Object class performs the task of cloning objects, the clone method in the House class simply invokes super.clone() . The clone method defined in the Object class may throw CloneNotSupportedException . Thus, super.clone() must be placed in a try-catch block. Exceptions and the try-catch block are introduced in Chapter 17, "Exceptions and Assertions."

The House class overrides the compareTo method (lines 36 “43) defined in the Comparable interface. The method compares the areas of two houses .

You can now create an object of the House class and create an identical copy from it, as follows:

 House house1 =   new   House(   1   ,   1750.50   ); House house2 = (House)house1.clone(); 

house1 and house2 are two different objects with identical contents. The clone method in the Object class copies each field from the original object to the target object. If the field is of a primitive type, its value is copied . For example, the value of area ( double type) is copied from house1 to house2 . If the field is of an object, the reference of the field is copied. For example, the field whenBuilt is of the Date class, so its reference is copied into house2 , as shown in Figure 10.6. Therefore, house1.whenBuilt == house2.whenBuilt is true , although house1 == house2 is false . This is referred to as a shallow copy rather than a deep copy , meaning that if the field is of an object, the reference of the field is copied rather than its contents.

Figure 10.6. The clone method copies the values of primitive type fields and the references of object type fields.
(This item is displayed on page 356 in the print version)

If you want to perform a deep copy, you can override the clone method with custom cloning operations instead of invoking super.clone() . See Exercise 10.4.


[Page 356]

Note

You learned how to use the arraycopy method to copy arrays in Chapter 6, "Arrays." This method provides shallow copies. It works fine for arrays of primitive data type elements, but not for arrays of object type elements. To support a deep copy, you have to deal with how to copy individual object elements in the array.


Caution

If the House class does not override the clone() method, the program will receive a syntax error because clone() is protected in java.lang.Object . If House does not implement java.lang.Cloneable , invoking super.clone() (line 28) in House.java would cause a CloneNotSupportedException . Thus, to enable cloning an object, the class for the object must override the clone() method and implement Cloneable .


 


Introduction to Java Programming-Comprehensive Version
Introduction to Java Programming-Comprehensive Version (6th Edition)
ISBN: B000ONFLUM
EAN: N/A
Year: 2004
Pages: 503

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