Vous êtes sur la page 1sur 7

PRG Notes for reading Week

Using Inheritance in Java


Inheritance is a mechanism in Java that allows you to extend a class, termed the base class or
superclass, with another class called the derived class or the subclass.

It often occurs in program design that a class with a basic set of features is extended in various
ways to make more specialised objects. One might design a text editor class which handled plain
text in the default font. This might then be extended to handle various font sizes as well as bold and
italic and to store its contents in rich-text format. A further extension might add the ability for
format paragraphs and to specify different fonts. This reflects sound programming practice which is
to start with a simple solution and to add detail and refinements later. The resulting hierarchy of
classes forms a tree structure.1 It is possible to extend a class anywhere in the hierarchy and thereby
to reuse all the code in the classes above that point.

Some problems fit this model of a class hierarchy very well indeed. For example the entire set of
swing classes use a deep hierarchy which is worthy thinking about as it is a tidy solution to a
complex problem. The diagram below shows this in UML:

1
Computer scientists draw trees upside down with the root at the top and the leaves at the bottom!

1
PRG Notes for reading Week

Another good example of a well-designed class hierarchy is that for Sockets in java. These are
classes which allow point to point communications to be established between ports on computers.
The classes higher in the tree are general, whilst those lowed down support specific protocols such
as HTTP.

The rather contrived examples in java text books tend to consider vehicles or animals, starting with
the lowest common denominator as the root and adding features such as turbochargers or wings at
the lowest levels. These illustrate the idea quite well, but are rather contrived from a programming
point of view. If you are designing software from scratch and can see a clear hierarchy of
abstraction in some aspect of it then you will almost certainly benefit from using an inheritance
hierarchy.2

So the main use of inheritance is to allow code reuse. The code of the classes at the top of a
hierarchy will be exploited by all those below. The user does not have to know how these classes
work to extend them. In Java a class can extend only one other class, a simplification which is
known as single inheritance.

The syntax for inheritance is as follows:

public class derived-class-name extends base-class-name {


// derived class methods extend and possibly override
// those of the base class
}

If a method in a derived class has the same signature as one in the base class, then it is said to
override that method.

A derived class can call methods in the baseclass it extends using the super keyword. This can be
very useful in practical examples. Suppose we have a class that draws lines:

public class draw_lines {


moveto(int x, int y) {… some implementation}
lineto(int x, int y) {… some implementation}
draw_line(int x0, int y0, int x1, int y1) {
moveto(x0, y0) ;
lineto(x1 ,y1) ;
}
}

It may be that we would like a class that draws a square. Perhaps we wish to do this in a method by
passing the lower left corner coordinate and the length of a side. It would be useful to reuse the

2
A personal note: I started at the other extreme as a programmer, and my first job was developing CAD packages
writing in assembly language. I was an early fan of object oriented programming using Smalltalk and C++. I
appreciated the ability to hide complexity and encapsulate my designs as classes. I was rather slow to use inheritance,
and it took me about five years before I began to spot opportunities to use this abstraction approach. It is more error-
prone in C++ which supports multiple inheritance, and it was Java and C# that have made this concept a natural way of
coding for me. I find that I use inheritance a great deal in C++ as well these days, which has improved my coding
style. C++ remains my language of choice for most programming other than server-side code and real-time embedded
work. Why? It gives more power to the programmer and I prefer to tidy up my own garbage than entrust it to the run-
time environment. I don’t really like my code being slowed down by a virtual machine either. This is not a fashionable
point of view and the trend is to cripple C++ as well… see managed C++ in MS dotNet.

2
PRG Notes for reading Week

class that draws lines and of course we could do that simply by including it in our CLASSPATH
and calling its methods. However it may well be a tidier solution to extend it like this:

public class draw_squares extends draw_lines {


draw_square(int x, int y, int side) {
super.draw_line(x, y, x + len, y);
super.draw_line(x + len, y, x + len, y + len);
super.draw_line(x + len, y + len, x, y + len);
super.draw_line(x, y + len, x, y);
}
}

The super keyword calls a method in the superclass.3 It can also be used in the constructor of the
derived class to call the superclass constructor, but in this case the super command must be the first
line of the constructor. One can see super as being a close relative of this. The first is a reference
to the baseclass object, the second to the current class object.

The Order of Construction under Inheritance

Note that when you construct an object, the default base class constructor is called implicitly,
before the body of the derived class constructor is executed. So, objects are constructed top-down
under inheritance. Since every object inherits from the original Object class, the Object()
constructor is always called implicitly. However, you can call a superclass constructor explicitly
using the built-in super keyword, as long as it is the first statement in a constructor.

For example, most Java exception objects inherit from the java.lang.Exception class. If you
wrote your own exception class, say SomeException, you might write it as follows:

public class SomeException extends Exception {


public SomeException() {
super(); // calls Exception(), which calls Object()
}
public SomeException(String s) {
super(s); // calls Exception(String),
}
public SomeException (int error_code) {
this("error”); // calls super(String)
System.err.println(error_code);
}
}

Abstract Base Classes


An abstract class is a class that leaves one or more method implementations unspecified by
declaring one or more methods abstract. An abstract method has no body (i.e., no

3
Exercise for the student: It is probably simpler to use the one moveto and four lineto()
calls using the super keyword to implement the method draw_square(). Rewrite the above
to do just that.

3
PRG Notes for reading Week

implementation). This means it is not possible to create an object of this class with the new
operator. A compiler error will occur if this is attempted. Sometimes such classes are known as
pure base classes. In Java terminology an abstract class is a better name. Many of the class
hierarchies in the Java libraries will have an abstract class at their root. Many people consider this
good programming practice.

In order to use an abstract base class, a subclass is required to override the abstract method or
methods and to provide an implementation. In other words, an abstract class is incomplete and
cannot be instantiated, but can be used as a base class that may be extended:

abstract public class abstract-base-class-name {


// abstract class has at least one abstract method
public abstract return-type abstract-method-name ( formal-
params );
... // other abstract methods, object methods, class methods
}

public class derived-class-name extends abstract-base-class-name {


public return-type abstract-method-name (formal-params) {
implementation of the method here; }
... // other method implementations
}

An example abstract class usage might be:

abstract class Point {


private int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public void move(int dx, int dy) {
x += dx; y += dy; plot();
}
public abstract void plot(); // has no implementation
}
abstract class ColouredPoint extends Point {
private int color;
protected public ColouredPoint(int x, int y, int color) {
super(x, y);
this.color = color;
}
}

class SimpleColouredPoint extends ColouredPoint {


public SimpleColouredPoint(int x, int y, int color) {
super(x,y,color);
}
public void plot() { ... } // code to plot a SimpleColouredPoint
}

The protected keyword in front of a variable ensures that this method is visible in the derived class
and can be accessed with the super. syntax. In fact if the classes are in the same package, or both
in the same directory and in no package at all, as has often been the case for PRG then it would be

4
PRG Notes for reading Week

visible in any case. The default scoping rules for Java are that anything in a public class is visible to
all other classes in the same package.

Note that you can only go up one level with super. It is not possible to write super.super….

Since ColouredPoint does not provide an implementation of the plot method, it must be
declared abstract. The SimpleColouredPoint class does implement the inherited plot method.
It would be an error to try to instantiate a Point object or a ColouredPoint object. However,
you can declare a Point reference and initialize it with an instance of a subclass object that
implements the plot method: Point p = new SimpleColouredPoint(a, b, red);
p.plot();

That is references to objects nearer the root in a class hierarchy may be used to point at less generic
classes further from the root, but not the other way round.

Interfaces

There is a second kind of inheritance in Java which is rather different and which implements the
concept of subtyping. In Java, the two kinds of inheritance are made distinct by using different
language syntax. For class inheritance, Java uses the keyword extends as discussed above, while
for interface inheritance Java uses the keyword implements.

An abstract class mixes the idea of data in the form of instance variables, non-abstract
methods, and abstract methods. An abstract class with only static final instance variables and
all abstract methods is called an interface. An interface is a specification, or contract, for a set of
methods that a class that implements the interface must conform to in terms of the type signature
of the methods. The class that implements the interface must provide an implementation for each
method, just as with an abstract method in an abstract class.

So, you can think of an interface as an abstract class with all abstract methods. The interface itself
can have either public, package, private or protected access defined. All methods declared in an
interface are implicitly abstract and implicitly public. It is not necessary, and in fact considered
redundant, to declare a method in an interface to be abstract.

You can define data in an interface, but it is less common to do so. If there are data fields defined in
an interface, then they are implicitly defined to be:

• public
• static
• final

In other words, any data defined in an interface are treated as public constants. Note that a class and
an interface in the same package cannot share the same name. Methods declared in an interface
cannot be declared final. Why not?

public interface interface-name {


// if any data are defined, they must be constants
public static final type-name var-name = constant-expr;
// one or more implicitly abstract and public methods

5
PRG Notes for reading Week

return-type method-name ( formal-params );


}

What is the point of using an interface? It turns out that an interface is a very useful programming
abstraction. It is rather like designing the front panel of an instrument, providing all the knobs,
buttons, dials, indicators and so on that a user would need. One can do that without thinking at all
about what goes on inside the box. This separation of concerns is a key idea to managing
complexity in systems design programming. A very common scenario in developing a system is to
specify an API or Application Programmer’s Interface for users of your work. The Java
documentation is full of references to APIs and it is a very useful idea. We met an API earlier in
looking at database access in Java using JDBC.

I am currently doing some consultancy for an organisation which requires me to segment and read
words from fax transmissions. I started with an API, expressed as an interface4 which indicated
the functionality of the system to be developed for the customer. Having done this, I can develop a
class that implements this interface and the customer can try it out. If I then decide to do it all in a
completely different way and write another class, provided it implements the same interface there
will be little or no need for the customer to change his code.

The idea of an interface proves to be even more fruitful than I can easily explain here at PRG level.
The idea or separating an interface from the code that implements it is the basis of all component
architectures including COM (Microsoft) and EJBs (Sun Java). It facilitates reuse of code at the
binary level which is what is really needed and avoids the mess Microsoft got themselves in with
Windows 3.1 and to some extent 95 which can be described as dll hell!5

Interface inheritance
A class may only extend one other class, but it may implements any number of interfaces.6 An
example of the syntax for using interfaces is:

public class class-name implements interface-name {


// class provides an implementation for the methods
// as specified by the interface
}

A stack is a very useful data structure in computer science. It is like a pile of things (see for
example my desk!) and one can only put things onto a stack at the top, and remove them from the
top (attempting anything else on my desk would cause a landslide). There are many possible ways
to implement such a data structure. One could use an array, or perhaps a linked list. The interface
can be the same in either case and thus it is a good idea to separate that from the implementation:

public interface StackInterface {


boolean empty();
void push(Object x);
Object pop() throws EmptyStackException;
Object peek() throws EmptyStackException;
}
4
In fact this was done as a virtual abstract baseclass in C++, but the principle is exactly the same
5
See http://www.cpcug.org/user/clemenzi/technical/Languages/DLL_Hell.htm
6
And throws any number of Exceptions

6
PRG Notes for reading Week

Note that the methods in this interface are defined to operate on objects of type Object. Since a
stack is a container type, it is very common to use the base class for all objects, namely Object,
as the type of the arguments and results to a container type. Since all objects ultimately inherit from
class Object, we can always generically refer to any object in the system using an Object
reference.However, when we pop from a stack, we have to explicitly type case from the very
general type Object to a concrete type, so that we can manipulate the object concretely.7

//Stack implementation of the StackInterface


public class Stack implements StackInterface {
private Vector v = new Vector();
public boolean empty() { return v.size() == 0; }
public void push(Object item) { v.addElement(item); }
public Object pop() {
Object obj = peek();
v.removeElementAt(v.size() - 1);
return obj;
}
public Object peek() throws EmptyStackException {
if (v.size() == 0) throw new EmptyStackException();
return v.elementAt(v.size() - 1);
}
}

When should you to use an Interface and when an abstract class? Having reviewed their basic
properties, there are two primary differences between interfaces and abstract classes:

• An abstract class can have a mix of abstract and non-abstract methods, so some default
implementations can be defined in the abstract base class. An abstract class can also have
static methods, static data, private and protected methods, etc. In other words, a class is a
class, so it can contain features inherent to a class. The downside to an abstract base class is
that since there is only single inheritance in Java, you can only inherit from one class.
• An interface has a very restricted use, namely, to declare a set of public abstract method
signatures that a subclass is required to implement. Since you can inherit multiple
interfaces, they are often a very useful mechanism to allow a class to implement a number
of standard APIs.

It is usually a good idea to implement an interface when you need to define methods that are to be
explicitly overridden by some subclass. If you then want some of the methods implemented with
default implementations that will be inherited by a subclass, then create an implementation class or
the interface, and have other class inherit (extend) that class, or just use an abstract base class
instead of an interface.

In the examination on Tuesday you might be asked to define a new class, to define an interface, to
extend a class, or to implement an interface. You may also be asked to write a testing method
which most often would be a main(String args[]).

7
Java 1.5 or Tiger Java introduces a way round this called generics. However this won’t compile under CourseMarker
as yet, so we will leave it out of the module. The same applies to enums – another very useful idea in 1,5.

Vous aimerez peut-être aussi