Chapter 5. Interfaces and Inheritance

YOU saw an example of implementing an interface in the previous chapter. You can read more about interfaces here—what they are for, why you might want to write one, and how to write one.

This chapter also describes the way in which you can derive one class from another, that is, how a subclass can inherit fields and methods from a superclass. You will learn that all classes are derived from the Object class, and how to modify the methods that a subclass inherits from superclasses. This chapter also covers interface-like abstract classes.

Interfaces

There are a number of situations in software engineering when it is important for disparate groups of programmers to agree to a “contract” that spells out how their software interacts. Each group should be able to write their code without any knowledge of how the other group’s code is written. Generally speaking, interfaces are such contracts.

For example, imagine a futuristic society where computer-controlled robotic cars transport passengers through city streets without a human operator. Automobile manufacturers write software (Java, of course) that operates the automobile—stop, start, accelerate, turn left, and so forth. Another industrial group, electronic guidance instrument manufacturers, make computer systems that receive GPS (Global Positioning Satellite) position data and wireless transmission of traffic conditions and use that information to drive the car.

The auto manufacturers must publish an industry-standard interface that spells out in detail what methods can be invoked to make the car move (any car, from any manufacturer). The guidance manufacturers can then write software that invokes the methods described in the interface to command the car. Neither industrial group needs to know how the other group’s software is implemented. In fact, each group considers its software highly proprietary and reserves the right to modify it at any time, as long as it continues to adhere to the published interface.

Interfaces in Java

In the Java programming language, an interface is a reference type, similar to a class, that can contain only constants, method signatures, and nested types. There are no method bodies. Interfaces cannot be instantiated—they can only be implemented by classes or extended by other interfaces. (Extension is discussed later in this chapter.)

Defining an interface is similar to creating a new class:

public interface OperateCar {

  // constant declarations, if any

  // method signatures
  int turn(Direction direction, // An enum with values
                                // RIGHT, LEFT
  double radius, double startSpeed, double endSpeed);
  int changeLanes(Direction direction,
                  double startSpeed, double endSpeed);
  int signalTurn(Direction direction, boolean signalOn);
  int getRadarFront(double distanceToCar, double speedOfCar);
  int getRadarRear(double distanceToCar, double speedOfCar);
    ...
  // more method signatures
}

Note that the method signatures have no braces and are terminated with a semicolon.

To use an interface, you write a class that implements the interface. When an instantiable class implements an interface, it provides a method body for each of the methods declared in the interface. For example,

public class OperateBMW760i implements OperateCar {

  // the OperateCar method signatures, with implementation --
  // for example:
  int signalTurn(Direction direction, boolean signalOn) {
    //code to turn BMW's LEFT turn indicator lights on
    //code to turn BMW's LEFT turn indicator lights off
    //code to turn BMW's RIGHT turn indicator lights on
    //code to turn BMW's RIGHT turn indicator lights off
  }

  // other members, as needed -- for example, helper classes
  // not visible to clients of the interface
}

In the robotic car example above, it is the automobile manufacturers who will implement the interface. Chevrolet’s implementation will be substantially different from that of Toyota, of course, but both manufacturers will adhere to the same interface. The guidance manufacturers, who are the clients of the interface, will build systems that use GPS data on a car’s location, digital street maps, and traffic data to drive the car. In so doing, the guidance systems will invoke the interface methods: turn, change lanes, brake, accelerate, and so forth.

Interfaces as APIs

The robotic car example shows an interface being used as an industry standard Application Programming Interface (API). APIs are also common in commercial software products. Typically, a company sells a software package that contains complex methods that another company wants to use in its own software product. An example would be a package of digital image processing methods that are sold to companies making enduser graphics programs. The image processing company writes its classes to implement an interface, which it makes public to its customers. The graphics company then invokes the image processing methods using the signatures and return types defined in the interface. While the image processing company’s API is made public (to its customers), its implementation of the API is kept as a closely guarded secret—in fact, it may revise the implementation at a later date as long as it continues to implement the original interface that its customers have relied on.

Interfaces and Multiple Inheritance

Interfaces have another very important role in the Java programming language. Interfaces are not part of the class hierarchy, although they work in combination with classes. The Java programming language does not permit multiple inheritance (inheritance is discussed later in this chapter), but interfaces provide an alternative.

In Java, a class can inherit from only one class but it can implement more than one interface. Therefore, objects can have multiple types: the type of their own class and the types of all the interfaces that they implement. This means that if a variable is declared to be the type of an interface, its value can reference any object that is instantiated from any class that implements the interface. This is discussed later in the Using an Interface as a Type section (page 145).

Defining an Interface

An interface declaration consists of modifiers, the keyword interface, the interface name, a comma-separated list of parent interfaces (if any), and the interface body. For example:

public interface GroupedInterface extends Interface1,
                                    Interface2, Interface3 {

  // constant declarations
  double E = 2.718282;  // base of natural logarithms

  // method signatures
  void doSomething (int i, double x);
  int doSomethingElse(String s);
}

The public access specifier indicates that the interface can be used by any class in any package. If you do not specify that the interface is public, your interface will be accessible only to classes defined in the same package as the interface.

An interface can extend other interfaces, just as a class can extend or subclass another class. However, whereas a class can extend only one other class, an interface can extend any number of interfaces. The interface declaration includes a comma-separated list of all the interfaces that it extends.

The Interface Body

The interface body contains method declarations for all the methods included in the interface. A method declaration within an interface is followed by a semicolon, but no braces, because an interface does not provide implementations for the methods declared within it. All methods declared in an interface are implicitly public, so the public modifier can be omitted.

An interface can contain constant declarations in addition to method declarations. All constant values defined in an interface are implicitly public, static, and final. Once again, these modifiers can be omitted.

Implementing an Interface

To declare a class that implements an interface, you include an implements clause in the class declaration. Your class can implement more than one interface, so the implements keyword is followed by a comma-separated list of the interfaces implemented by the class.

By convention, the implements clause follows the extends clause, if there is one.

A Sample Interface, Relatable

Consider an interface that defines how to compare the size of objects.

public interface Relatable {

  // this (object calling isLargerThan) and
  // other must be instances of the same class
  // returns 1, 0, -1 if this is greater
  // than, equal to, or less than other
  public int isLargerThan(Relatable other);
}

If you want to be able to compare the size of similar objects, no matter what they are, the class that instantiates them should implement Relatable.

Any class can implement Relatable if there is some way to compare the relative “size” of objects instantiated from the class. For strings, it could be number of characters; for books, it could be number of pages; for students, it could be weight; and so forth. For planar geometric objects, area would be a good choice (see the RectanglePlus class that follows), while volume would work for three-dimensional geometric objects. All such classes can implement the isLargerThan() method.

If you know that a class implements Relatable, then you know that you can compare the size of the objects instantiated from that class.

Implementing the Relatable Interface

Here is the Rectangle class that was presented in the Creating Objects section (page 99), rewritten to implement Relatable:

public class RectanglePlus implements Relatable {
  public int width = 0;
  public int height = 0;
  public Point origin;

  // four constructors
  public RectanglePlus() {
    origin = new Point(0, 0);
  }
  public RectanglePlus(Point p) {
    origin = p;
  }
  public RectanglePlus(int w, int h) {
    origin = new Point(0, 0);
    width = w;
    height = h;
  }
  public RectanglePlus(Point p, int w, int h) {
    origin = p;
    width = w;
    height = h;
  }

  // a method for moving the rectangle
  public void move(int x, int y) {
    origin.x = x;
    origin.y = y;
  }

  // a method for computing the area of the rectangle
  public int getArea() {
    return width * height;
  }

  // a method to implement Relatable
  public int isLargerThan(Relatable other) {
    RectanglePlus otherRect = (RectanglePlus)other;
    if (this.getArea() < otherRect.getArea())
      return -1;
    else if (this.getArea() > otherRect.getArea())
      return 1;
    else
      return 0;
  }
}

Because RectanglePlus implements Relatable, the size of any two RectanglePlus objects can be compared.

Using an Interface as a Type

When you define a new interface, you are defining a new reference data type. You can use interface names anywhere you can use any other data type name. If you define a reference variable whose type is an interface, any object you assign to it must be an instance of a class that implements the interface.

As an example, here is a method for finding the largest object in a pair of objects, for any objects that are instantiated from a class that implements Relatable:

public Object findLargest(Object object1, Object object2) {
  Relatable obj1 = (Relatable)object1;
  Relatable obj2 = (Relatable)object2;
  if ( (obj1).isLargerThan(obj2) > 0)
    return object1;
  else
    return object2;
}

By casting object1 to a Relatable type, it can invoke the isLargerThan method.

If you make a point of implementing Relatable in a wide variety of classes, the objects instantiated from any of those classes can be compared with the findLargest() method—provided that both objects are of the same class. Similarly, they can all be compared with the following methods:

public Object findSmallest(Object object1, Object object2) {
  Relatable obj1 = (Relatable)object1;
  Relatable obj2 = (Relatable)object2;
  if ( (obj1).isLargerThan(obj2) < 0)
    return object1;
  else
    return object2;
}

public boolean isEqual(Object object1, Object object2) {
  Relatable obj1 = (Relatable)object1;
  Relatable obj2 = (Relatable)object2;
  if ( (obj1).isLargerThan(obj2) == 0)
    return true;
  else
    return false;
}

These methods work for any “relatable” objects, no matter what their class inheritance is. When they implement Relatable, they can be of both their own class (or superclass) type and a Relatable type. This gives them some of the advantages of multiple inheritance, where they can have behavior from both a superclass and an interface.

Rewriting Interfaces

Consider an interface that you have developed called DoIt:

public interface DoIt {
  void doSomething(int i, double x);
  int doSomethingElse(String s);
}

Suppose that, at a later time, you want to add a third method to DoIt, so that the interface now becomes:

public interface DoIt {
  void doSomething(int i, double x);
  int doSomethingElse(String s);
  boolean didItWork(int i, double x, String s);
}

If you make this change, all classes that implement the old DoIt interface will break because they don’t implement the interface anymore. Programmers relying on this interface will protest loudly.

Try to anticipate all uses for your interface and to specify it completely from the beginning. Given that this is often impossible, you may need to create more interfaces later. For example, you could create a DoItPlus interface that extends DoIt:

public interface DoItPlus extends DoIt {

  boolean didItWork(int i, double x, String s);
}

Now users of your code can choose to continue to use the old interface or to upgrade to the new interface.

Summary of Interfaces

An interface defines a protocol of communication between two objects.

An interface declaration contains signatures, but no implementations, for a set of methods, and might also contain constant definitions.

A class that implements an interface must implement all the methods declared in the interface.

An interface name can be used anywhere a type can be used.

Questions and Exercises: Interfaces

Questions

1.

What methods would a class that implements the java.lang.CharSequence[1] interface have to implement?

2.

What is wrong with the following interface?

public interface SomethingIsWrong {
  void aMethod(int aValue){
    System.out.println("Hi Mom");
  }
}

3.

Fix the interface in Question 2.

4.

Is the following interface valid?

public interface Marker {
}

Exercises

1.

Write a class that implements the CharSequence interface found in the java.lang package. Select one of the sentences from this book to use as the data. Write a small main method to test your class; make sure to call all four methods.

2.

Suppose you have written a time server that periodically notifies its clients of the current date and time. Write an interface the server could use to enforce a particular protocol on its clients.

Answers

You can find answers to these Questions and Exercises at:

tutorial/java/IandI/QandE/interfaces-answers.html

Inheritance

In the preceding chapters, you have seen inheritance mentioned several times. In the Java language, classes can be derived from other classes, thereby inheriting fields and methods from those classes.

Definitions

A class that is derived from another class is called a subclass (also a derived class, extended class, or child class). The class from which the subclass is derived is called a superclass (also a base class or a parent class).

Excepting Object, which has no superclass, every class has one and only one direct superclass (single inheritance). In the absence of any other explicit superclass, every class is implicitly a subclass of Object.

Classes can be derived from classes that are derived from classes that are derived from classes, and so on, and ultimately derived from the topmost class, Object. Such a class is said to be descended from all the classes in the inheritance chain stretching back to Object.

The idea of inheritance is simple but powerful: When you want to create a new class and there is already a class that includes some of the code that you want, you can derive your new class from the existing class. In doing this, you can reuse the fields and methods of the existing class without having to write (and debug!) them yourself.

A subclass inherits all of the public and protected members (fields, methods, and nested classes) of its superclass no matter what package the subclass is in. If the subclass is in the same package as its parent, it also inherits the package-private members of the superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.

The Java Platform Class Hierarchy

The Object class,[2] defined in the java.lang package, defines and implements behavior common to all classes—including the ones that you write. In the Java platform, many classes derive directly from Object, other classes derive from some of those classes, and so on, forming a hierarchy of classes (Figure 5.1).

All classes in the Java platform are descendants of Object.

Figure 5.1. All classes in the Java platform are descendants of Object.

At the top of the hierarchy, Object is the most general of all classes. Classes near the bottom of the hierarchy provide more specialized behavior.

An Example of Inheritance

Here is the sample code for a possible implementation of a Bicycle class that was presented in Chapter 4:

public class Bicycle {

  // the Bicycle class has three fields
  public int cadence;
  public int gear;
  public int speed;

  // the Bicycle class has one constructor
  public Bicycle(int startCadence,
                 int startSpeed, int startGear) {
    gear = startGear;
    cadence = startCadence;
    speed = startSpeed;
  }

  // the Bicycle class has four methods
  public void setCadence(int newValue) {
    cadence = newValue;
  }

  public void setGear(int newValue) {
    gear = newValue;
  }

  public void applyBrake(int decrement) {
    speed -= decrement;
  }

  public void speedUp(int increment) {
    speed += increment;
  }
}

A class declaration for a MountainBike class that is a subclass of Bicycle might look like this:

public class MountainBike extends Bicycle {

  // the MountainBike subclass adds one field
  public int seatHeight;

  // the MountainBike subclass has one constructor

  public MountainBike(int startHeight, int startCadence,
                            int startSpeed, int startGear) {
    super(startCadence, startSpeed, startGear);
    seatHeight = startHeight;
  }

  // the MountainBike subclass adds one method
  public void setHeight(int newValue) {
    seatHeight = newValue;
  }
}

MountainBike inherits all the fields and methods of Bicycle and adds the field seatHeight and a method to set it. Except for the constructor, it is as if you had written a new MountainBike class entirely from scratch, with four fields and five methods. However, you didn’t have to do all the work. This would be especially valuable if the methods in the Bicycle class were complex and had taken substantial time to debug.

What You Can Do in a Subclass

A subclass inherits all of the public and protected members of its parent, no matter what package the subclass is in. If the subclass is in the same package as its parent, it also inherits the package-private members of the parent. You can use the inherited members as is, replace them, hide them, or supplement them with new members:

  • The inherited fields can be used directly, just like any other fields.

  • You can declare a field in the subclass with the same name as the one in the superclass, thus hiding it (not recommended).

  • You can declare new fields in the subclass that are not in the superclass.

  • The inherited methods can be used directly as they are.

  • You can write a new instance method in the subclass that has the same signature as the one in the superclass, thus overriding it.

  • You can write a new static method in the subclass that has the same signature as the one in the superclass, thus hiding it.

  • You can declare new methods in the subclass that are not in the superclass.

  • You can write a subclass constructor that invokes the constructor of the superclass, either implicitly or by using the keyword super.

The following sections in this chapter will expand on these topics.

Private Members in a Superclass

A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass.

A nested class has access to all the private members of its enclosing class—both fields and methods. Therefore, a public or protected nested class inherited by a subclass has indirect access to all of the private members of the superclass.

Casting Objects

We have seen that an object is of the data type of the class from which it was instantiated. For example, if we write:

public MountainBike myBike = new MountainBike();

then myBike is of type MountainBike.

MountainBike is descended from Bicycle and Object. Therefore, a MountainBike is a Bicycle and is also an Object, and it can be used wherever Bicycle or Object objects are called for.

The reverse is not necessarily true: a Bicycle may be a MountainBike, but it isn’t necessarily. Similarly, an Object may be a Bicycle or a MountainBike, but it isn’t necessarily.

Casting shows the use of an object of one type in place of another type, among the objects permitted by inheritance and implementations. For example, if we write:

Object obj = new MountainBike();

then obj is both an Object and a Mountainbike (until such time as obj is assigned another object that is not a Mountainbike). This is called implicit casting.

If, on the other hand, we write:

MountainBike myBike = obj;

we would get a compile-time error because obj is not known to the compiler to be a MountainBike. However, we can tell the compiler that we promise to assign a MountainBike to obj by explicit casting:

MountainBike myBike = (MountainBike)obj;

This cast inserts a runtime check that obj is assigned a MountainBike so that the compiler can safely assume that obj is a MountainBike. If obj is not a Mountainbike at runtime, an exception will be thrown.

Note

You can make a logical test as to the type of a particular object using the instanceof operator. This can save you from a runtime error owing to an improper cast. For example:

if (obj instanceof MountainBike) {
   MountainBike myBike = (MountainBike)obj;
}

Here the instanceof operator verifies that obj refers to a MountainBike so that we can make the cast with knowledge that there will be no runtime exception thrown.

Overriding and Hiding Methods

Instance Methods

An instance method in a subclass with the same signature (name, plus the number and the type of its parameters) and return type as an instance method in the superclass overrides the superclass’s method.

The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is “close enough” and then to modify behavior as needed. The overriding method has the same name, number and type of parameters, and return type as the method it overrides. An overriding method can also return a subtype of the type returned by the overridden method. This is called a covariant return type.

When overriding a method, you might want to use the @Override annotation that instructs the compiler that you intend to override a method in the superclass. If, for some reason, the compiler detects that the method does not exist in one of the superclasses, it will generate an error. For more information on @Override, see the Annotations section (page 132).

Class Methods

If a subclass defines a class method with the same signature as a class method in the superclass, the method in the subclass hides the one in the superclass.

The distinction between hiding and overriding has important implications. The version of the overridden method that gets invoked is the one in the subclass. The version of the hidden method that gets invoked depends on whether it is invoked from the superclass or the subclass. Let’s look at an example that contains two classes. The first is Animal, which contains one instance method and one class method:

public class Animal {
  public static void testClassMethod() {
    System.out.println("The class method in Animal.");
  }
  public void testInstanceMethod() {
    System.out.println("The instance method in Animal.");
  }
}

The second class, a subclass of Animal, is called Cat:

public class Cat extends Animal {
  public static void testClassMethod() {
    System.out.println("The class method in Cat.");
  }
  public void testInstanceMethod() {
    System.out.println("The instance method in Cat.");
  }

  public static void main(String[] args) {
    Cat myCat = new Cat();
    Animal myAnimal = myCat;
    Animal.testClassMethod();
    myAnimal.testInstanceMethod();
  }
}

The Cat class overrides the instance method in Animal and hides the class method in Animal. The main method in this class creates an instance of Cat and calls testClassMethod() on the class and testInstanceMethod() on the instance.

The output from this program is as follows:

The class method in Animal.
The instance method in Cat.

As promised, the version of the hidden method that gets invoked is the one in the superclass, and the version of the overridden method that gets invoked is the one in the subclass.

Modifiers

The access specifier for an overriding method can allow more, but not less, access than the overridden method. For example, a protected instance method in the superclass can be made public, but not private, in the subclass.

You will get a compile-time error if you attempt to change an instance method in the superclass to a class method in the subclass, and vice versa.

Summary

Table 5.1 summarizes what happens when you define a method with the same signature as a method in a superclass.

Table 5.1. Defining a Method with the Same Signature as a Superclass’s Method

Superclass Instance Method

Superclass Static Method

Subclass Instance Method

Overrides

Generates a compile-time error

Subclass Static Method

Generates a compile-time error

Hides

Note

In a subclass, you can overload the methods inherited from the superclass. Such overloaded methods neither hide nor override the superclass methods—they are new methods, unique to the subclass.

Hiding Fields

Within a class, a field that has the same name as a field in the superclass hides the superclass’s field, even if their types are different. Within the subclass, the field in the superclass cannot be referenced by its simple name. Instead, the field must be accessed through super, which is covered in the next section. Generally speaking, we don’t recommend hiding fields as it makes code difficult to read.

Using the Keyword super

Accessing Superclass Members

If your method overrides one of its superclass’s methods, you can invoke the overridden method through the use of the keyword super. You can also use super to refer to a hidden field (although hiding fields is discouraged). Consider this class, Superclass:

public class Superclass {

  public void printMethod() {
    System.out.println("Printed in Superclass.");
  }
}

Here is a subclass, called Subclass, that overrides printMethod():

public class Subclass extends Superclass {

  public void printMethod() {
    //overrides printMethod in Superclass
    super.printMethod();
    System.out.println("Printed in Subclass");
  }
  public static void main(String[] args) {

  Subclass s = new Subclass();
  s.printMethod();
  }

}

Within Subclass, the simple name printMethod() refers to the one declared in Subclass, which overrides the one in Superclass. So, to refer to printMethod() inherited from Superclass, Subclass must use a qualified name, using super as shown. Compiling and executing Subclass prints the following:

Printed in Superclass.
Printed in Subclass

Subclass Constructors

The following example illustrates how to use the super keyword to invoke a superclass’s constructor. Recall from the Bicycle example (in the Inheritance section, page 147) that MountainBike is a subclass of Bicycle. Here is the MountainBike (subclass) constructor that calls the superclass constructor and then adds initialization code of its own:

public MountainBike(int startHeight, int startCadence,
                          int startSpeed, int startGear) {
  super(startCadence, startSpeed, startGear);
  seatHeight = startHeight;
}

Invocation of a superclass constructor must be the first line in the subclass constructor.

The syntax for calling a superclass constructor is:

super();

or

super(parameter list);

With super(), the superclass no-argument constructor is called. With super(parameter list), the superclass constructor with a matching parameter list is called.

Note

If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the superclass does not have a no-argument constructor, you will get a compile-time error. Object does have such a constructor, so if Object is the only superclass, there is no problem.

If a subclass constructor invokes a constructor of its superclass, either explicitly or implicitly, you might think that there will be a whole chain of constructors called, all the way back to the constructor of Object. In fact, this is the case. It is called constructor chaining, and you need to be aware of it when there is a long line of class descent.

Object as a Superclass

The Object class, in the java.lang package, sits at the top of the class hierarchy tree. Every class is a descendant, direct or indirect, of the Object class. Every class you use or write inherits the instance methods of Object. You need not use any of these methods, but, if you choose to do so, you may need to override them with code that is specific to your class. The methods inherited from Object that are discussed in this section are:

protected Object clone() throws CloneNotSupportedExceptionCreates and returns a copy of this object.

public boolean equals(Object obj)Indicates whether some other object is “equal to” this one.

protected void finalize() throws ThrowableCalled by the garbage collector on an object when garbage collection determines that there are no more references to the object.

public final Class getClass()Returns the runtime class of an object.

public int hashCode()Returns a hash code value for the object.

public String toString()Returns a string representation of the object.

The notify, notifyAll, and wait methods of Object all play a part in synchronizing the activities of independently running threads in a program, which is discussed in Chapter 12 and won’t be covered here. There are five of these methods:

  • public final void notify()

  • public final void notifyAll()

  • public final void wait()

  • public final void wait(long timeout)

  • public final void wait(long timeout, int nanos)

Note

There are some subtle aspects to a number of these methods, especially the clone method. You can get information on the correct usage of these methods in the book Effective Java[3] by Josh Bloch.

The clone() Method

If a class, or one of its superclasses, implements the Cloneable interface, you can use the clone() method to create a copy from an existing object. To create a clone, you write:

aCloneableObject.clone();

Object’s implementation of this method checks to see whether the object on which clone() was invoked implements the Cloneable interface. If the object does not, the method throws a CloneNotSupportedException exception. Exception handling will be covered in Chapter 9. For the moment, you need to know that clone() must be declared as:

protected Object clone() throws CloneNotSupportedException

or

public Object clone() throws CloneNotSupportedException

if you are going to write a clone() method to override the one in Object.

If the object on which clone() was invoked does implement the Cloneable interface, Object’s implementation of the clone() method creates an object of the same class as the original object and initializes the new object’s member variables to have the same values as the original object’s corresponding member variables.

The simplest way to make your class cloneable is to add implements Cloneable to your class’s declaration. Then your objects can invoke the clone() method.

For some classes, the default behavior of Object’s clone() method works just fine. If, however, an object contains a reference to an external object, say ObjExternal, you may need to override clone() to get correct behavior. Otherwise, a change in ObjExternal made by one object will be visible in its clone also. This means that the original object and its clone are not independent—to decouple them, you must override clone() so that it clones the object and ObjExternal. Then the original object references ObjExternal and the clone references a clone of ObjExternal, so that the object and its clone are truly independent.

The equals() Method

The equals() method compares two objects for equality and returns true if they are equal. The equals() method provided in the Object class uses the identity operator (==) to determine whether two objects are equal. For primitive data types, this gives the correct result. For objects, however, it does not. The equals() method provided by Object tests whether the object references are equal—that is, if the objects compared are the exact same object.

To test whether two objects are equal in the sense of equivalency (containing the same information), you must override the equals() method. Here is an example of a Book class that overrides equals():

public class Book {
  ...
  public boolean equals(Object obj) {
    if (obj instanceof Book)
      return ISBN.equals((Book)obj.getISBN());
    else
      return false;
  }
}

Consider this code that tests two instances of the Book class for equality:

Book firstBook = new Book("0201914670");
  // Swing Tutorial, 2nd edition
Book secondBook = new Book("0201914670");
if (firstBook.equals(secondBook)) {
  System.out.println("objects are equal");
}
else {
  System.out.println("objects are not equal");
}

This program displays objects are equal even though firstBook and secondBook reference two distinct objects. They are considered equal because the objects compared contain the same ISBN.

You should always override the equals() method if the identity operator is not appropriate for your class.

Note

If you override equals(), you must override hashCode() as well.

The finalize() Method

The Object class provides a callback method, finalize(), that may be invoked on an object when it becomes garbage. Object’s implementation of finalize() does nothing—you can override finalize() to do cleanup, such as freeing resources.

The finalize() method may be called automatically by the system, but when it is called, or even if it is called, is uncertain. Therefore, you should not rely on this method to do your cleanup for you. For example, if you don’t close file descriptors in your code after performing I/O and you expect finalize() to close them for you, you may run out of file descriptors.

The getClass() Method

You cannot override getClass.

The getClass() method returns a Class object, which has methods you can use to get information about the class, such as its name (getSimpleName()), its superclass (getSuperclass()), and the interfaces it implements (getInterfaces()). For example, the following method gets and displays the class name of an object:

void printClassName(Object obj) {
  System.out.println("The object's class is "
                       obj.getClass().getSimpleName());
}

The Class class,[4] in the java.lang package, has a large number of methods (more than fifty). For example, you can test to see if the class is an annotation (isAnnotation()), an interface (isInterface()), or an enumeration (isEnum()). You can see what the object’s fields are (getFields()) or what its methods are (getMethods()), and so on.

The hashCode() Method

The value returned by hashCode() is the object’s hash code, which is the object’s memory address in hexadecimal.

By definition, if two objects are equal, their hash code must also be equal. If you override the equals() method, you change the way two objects are equated and Object’s implementation of hashCode() is no longer valid. Therefore, if you override the equals() method, you must also override the hashCode() method as well.

The toString() Method

You should always consider overriding the toString() method in your classes.

The Object’s toString() method returns a String representation of the object, which is very useful for debugging. The String representation for an object depends entirely on the object, which is why you need to override toString() in your classes.

You can use toString() along with System.out.println() to display a text representation of an object, such as an instance of Book:

System.out.println(firstBook.toString());

which would, for a properly overridden toString() method, print something useful, like this:

ISBN: 0201914670; The JFC Swing Tutorial; A Guide to
                             Constructing GUIs, 2nd Edition

Writing Final Classes and Methods

You can declare some or all of a class’s methods final. You use the final keyword in a method declaration to indicate that the method cannot be overridden by subclasses. The Object class does this—a number of its methods are final.

You might wish to make a method final if it has an implementation that should not be changed and it is critical to the consistent state of the object. For example, you might want to make the getFirstPlayer method in this ChessAlgorithm class final:

class ChessAlgorithm {
  enum ChessPlayer { WHITE, BLACK }
  ...
  final ChessPlayer getFirstPlayer() {
    return ChessPlayer.WHITE;
  }
  ...
}

Methods called from constructors should generally be declared final. If a constructor calls a non-final method, a subclass may redefine that method with surprising or undesirable results.

Note that you can also declare an entire class final—this prevents the class from being subclassed. This is particularly useful, for example, when creating an immutable class like the String class.

Abstract Methods and Classes

An abstract class is a class that is declared abstract—it may or may not include abstract methods. Abstract classes cannot be instantiated, but they can be subclassed.

An abstract method is a method that is declared without an implementation (without braces, and followed by a semicolon), like this:

abstract void moveTo(double deltaX, double deltaY);

If a class includes abstract methods, the class itself must be declared abstract, as in:

public abstract class GraphicObject {
  // declare fields
  // declare non-abstract methods
  abstract void draw();
}

When an abstract class is subclassed, the subclass usually provides implementations for all of the abstract methods in its parent class. However, if it does not, the subclass must also be declared abstract.

Note

All of the methods in an interface, see the Interfaces section (page 139), are implicitly abstract, so the abstract modifier is not used with interface methods (it could be—it’s just not necessary).

Abstract Classes versus Interfaces

Unlike interfaces, abstract classes can contain fields that are not static and final, and they can contain implemented methods. Such abstract classes are similar to interfaces, except that they provide a partial implementation, leaving it to subclasses to complete the implementation. If an abstract class contains only abstract method declarations, it should be declared as an interface instead.

Multiple interfaces can be implemented by classes anywhere in the class hierarchy, whether or not they are related to one another in any way. Think of Comparable or Cloneable, for example.

By comparison, abstract classes are most commonly subclassed to share pieces of implementation. A single abstract class is subclassed by similar classes that have a lot in common (the implemented parts of the abstract class), but also have some differences (the abstract methods).

An Abstract Class Example

In an object-oriented drawing application, you can draw circles, rectangles, lines, Bezier curves, and many other graphic objects. These objects all have certain states (for example: position, orientation, line color, fill color) and behaviors (for example: moveTo, rotate, resize, draw) in common. Some of these states and behaviors are the same for all graphic objects—for example: position, fill color, and moveTo. Others require different implementations—for example, resize or draw. All GraphicObjects must know how to draw or resize themselves; they just differ in how they do it.

This is a perfect situation for an abstract superclass. You can take advantage of the similarities and declare all the graphic objects to inherit from the same abstract parent object—for example, GraphicObject, as shown in Figure 5.2.

Classes Rectangle, Line, Bezier, and Circle inherit from GraphicObject.

Figure 5.2. Classes Rectangle, Line, Bezier, and Circle inherit from GraphicObject.

First, you declare an abstract class, GraphicObject, to provide member variables and methods that are wholly shared by all subclasses, such as the current position and the moveTo method. GraphicObject also declares abstract methods for methods, such as draw or resize, that need to be implemented by all subclasses but must be implemented in different ways. The GraphicObject class can look something like this:

abstract class GraphicObject {
  int x, y;
  ...
  void moveTo(int newX, int newY) {
    ...
  }
  abstract void draw();
  abstract void resize();
}

Each non-abstract subclass of GraphicObject, such as Circle and Rectangle, must provide implementations for the draw and resize methods:

class Circle extends GraphicObject {
  void draw() {
    ...
  }
  void resize() {
    ...
  }
}
class Rectangle extends GraphicObject {
  void draw() {
    ...
  }
  void resize() {
    ...
  }
}

When an Abstract Class Implements an Interface

In the Interfaces section (page 139), it was noted that a class that implements an interface must implement all of the interface’s methods. It is possible, however, to define a class that does not implement all of the interface methods, provided that the class is declared to be abstract. For example,

abstract class X implements Y {
  // implements all but one method of Y
}

class XX extends X {
  // implements the remaining method in Y
}

In this case, class X must be abstract because it does not fully implement Y, but class XX does, in fact, implement Y.

Class Members

An abstract class may have static fields and static methods. You can use these static members with a class reference—for example, AbstractClass. StaticMethod()—as you would with any other class.

Summary of Inheritance

Except for the Object class, a class has exactly one direct superclass. A class inherits fields and methods from all its superclasses, whether direct or indirect. A subclass can override methods that it inherits, or it can hide fields or methods that it inherits. (Note that hiding fields is generally bad programming practice.)

Table 5.1 in the Overriding and Hiding Methods section (page 152) shows the effect of declaring a method with the same signature as a method in the superclass.

The Object class is the top of the class hierarchy. All classes are descendants from this class and inherit methods from it. Useful methods inherited from Object include toString(), equals(), clone(), and getClass().

You can prevent a class from being subclassed by using the final keyword in the class’s declaration. Similarly, you can prevent a method from being overridden by subclasses by declaring it as a final method.

An abstract class can only be subclassed; it cannot be instantiated. An abstract class can contain abstract methods—methods that are declared but not implemented. Subclasses then provide the implementations for the abstract methods.

Questions and Exercises: Inheritance

Questions

1.

Consider the following two classes:

public class ClassA {
  public void methodOne(int i) {
  }
  public void methodTwo(int i) {
  }
  public static void methodThree(int i) {
  }
  public static void methodFour(int i) {
  }
}

public class ClassB extends ClassA {
  public static void methodOne(int i) {
  }
  public void methodTwo(int i) {
  }
  public void methodThree(int i) {
  }
  public static void methodFour(int i) {
  }
}
  1. Which method overrides a method in the superclass?

  2. Which method hides a method in the superclass?

  3. What do the other methods do?

2.

Consider the Card,[5] Deck,[6] and DisplayDeck[7] classes you wrote in Questions and Exercises: Classes (page 119). What Object methods should each of these classes override?

Exercises

Write the implementations for the methods that you answered in question 2.

Answers

You can find answers to these Questions and Exercises at:

tutorial/java/IandI/QandE/inherit-answers.html


[1] docs/api/java/lang/CharSequence.html

[2] docs/api/java/lang/Object.html

[3] docs/books/effective

[4] docs/api/java/lang/Class.html

[5] tutorial/java/IandI/QandE/Card.java

[6] tutorial/java/IandI/QandE/Deck.java

[7] tutorial/java/IandI/QandE/DisplayDeck.java

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

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