• No results found

More OOP

In document Java (Page 192-200)

This part covers further aspects of object-oriented programming.

abstract

This section looks at inheritance and abstract in some depth. The basic idea is to reuse the code in a base class or superclass and extend it to give more capabilities in a subclass.

Rules of inheritance

 A subclass extends a superclass (base class)

 The subclass inherits all the data fields and methods from the superclass.

Constructors are not inherited

 The subclass can add more data fields and methods.

 The subclass can have a different version of an inherited method with the same name. The subclass method over-rides the superclass version.

 The subclass can itself be extended to another level. This results in a class hierarchy.

At the top of all class hierarchies is the class Object. In other words all classes extend Object.

super

Constructors are not inherited - in the base class, constructors need to be written. However it is possible for the base class constructor to invoke the super class constructor, by

super();

for the constructor with no arguments (the 'no-arg constructor') or something like

super(x,y,z);

to invoke a constructor with arguments.

If there is a call to super, it must be the first statement in the constructor.

If you do not call super, the compiler will in effect insert one. In other words the compiler will generate bytecode so that the base constructor first calls the super constructor.

The super notation can also be used to refer to superclass fields and methods where they have been over-ridden. For example suppose we have a base class Base with a method someMethod, and a sub-class Sub has its own version of someMethod. This will over-ride the base class version, so inside Sub,

someMethod();

invokes Sub's version. But if we need to we can say

super.someMethod();

and it will invoke the base class version.

Class hierarchy design

Much Java programming involves just using existing classes, or possibly sub-classing one class to give an adapted version. However we may sometimes need to design a hierarchy with several levels. In this case:

Common features go in the base class

Features which are logically required by all go in the base class A specialisation is a subclass of a base class.

Classes towards the top of the hierarchy may be so general that they cannot logically be instantiated. This can be controlled by declaring them to be abstract.

Example - a Shape hierarchy

We will use the Java 2D Graphics API to set up the following

What do all shape objects have in common? A position and size, a graphics context to be drawn in, line and fill colours (ignoring gradients), and a method to draw it on the screen. These will therefore go in the Shape class. A circle is a specialisation of an oval, with width and height equal, and a square is a special oblong. We cannot actually draw a Shape, not knowing what shape it is. We can ensure this by making Shape an abstract class.

Base classes do not have to be abstract. For example Oval is a base class for Circle, but it is not abstract.

The definition of Shape is:

abstract class Shape { Color lineColor;

Color fillColor;

Graphics2D graphicsContext;

int width;

class Oval extends Shape { Ellipse2D.Double oval;

oval=new Ellipse2D.Double(left,top, width, height);

}

Oval(Graphics2D g, int top, int left, int width, int height, Color lineColor, Color fillColor)

oval=new Ellipse2D.Double(left,top, width, height);

}

void show() {

graphicsContext.setPaint(fillColor);

graphicsContext.fill(oval);

graphicsContext.setColor(lineColor);

graphicsContext.draw(oval);

} }

Circle is just:

class Circle extends Oval {

Circle(Graphics2D g, int top, int left, int radius, Color lineColor, Color fillColor) {

super(g, top, left, radius, radius, lineColor, fillColor);

} }

Code to use these classes could be

Container pane = getContentPane();

Graphics2D g2 = (Graphics2D) pane.getGraphics();

Oval oval1 = new Oval(g2);

oval1.show();

Oval oval2 = new Oval(g2, 50,50,80,30, Color.red, Color.blue);

oval2.show();

Circle circle = new Circle(g2, 0, 80, 30, Color.green, Color.yellow);

circle.show();

Shape has a simple constructor to establish the graphics context, and an abstract show method. This means that all non-abstract sub-classes of Shape must implement the show() method, and that we cannot accidentally instantiate a Shape object - the compiler will stop us.

The Oval class has two constructors - this is overloading. The one with just one argument calls the constructor of Shape (through super) to establish the graphics context, and gives some default values to the width and so on. The other constructor is similar, but also passes in values for width, colour and so on. The Oval class defines show, so we can instantiate it and it is not abstract.

Why does Oval not declare top, left and the other fields? Because they are inherited from Shape.

The Circle class is very similar to Oval. The difference is worked by the constructor, whose radius parameter is passed to the width and height parameters of the underlying Oval.

Why does Circle not have a show method defined? Because it inherits show from Oval.

Exercise

Run this application.

Accessor methods would be getHeight, setHeight, getTop, setTop and so on. In which class or classes would you define them? Do it and use them.

Define the Oblong and Square classes and use them.

Multiple inheritance

Suppose we want a class to extend two base classes, because we want it to develop the functionality of two classes. We could try

class MyClass extends ClassA, ClassB ..

but the compiler will not let you. Java does not allow multiple inheritance in this way (C++ does).

But you can do this by using inheritance by composition. This means having a data field in the class which is an instance of the second class. This would be

class MyClass extends ClassA {

ClassB bObject;

..

Now the bOject knows how to do ClassB methods, and so MyClass has both ClassA and ClassB features.

The graphics classes show this. How can Oval actually draw an oval shape? We could have tried to draw ovals 'from scratch' in the definition of Oval, but the Ellipse2D.Double class already knows how to do this. So we started Oval with:

class Oval extends Shape { Ellipse2D.Double oval;

which means Oval inherits from Shape and knows how to do Ellipse2D.Double methods.

This is not full multiple inheritance, since we cannot over-ride the methods of the composed class.

For example, Oval cannot alter the methods of Ellipse2D.Double. But it can use them in different ways, which is very close.

These example classes show another case of inheritance by composition, with the Shape class containing a Graphics2D object. It is this which actually knows how to outline and fill an ellipse, which we use in

void show() {

graphicsContext.setPaint(fillColor);

graphicsContext.fill(oval);

graphicsContext.setColor(lineColor);

graphicsContext.draw(oval);

}

Check your understanding What does inheritance mean?

What is the Object class?

What is the difference between overriding and overloading?

What is gained by declaring a class abstract?

What does 'inheritance by composition' mean?

Annotations

An annotation provides some information about the program - meta-data about the program.

Annotations do not directly influence execution, but they might indirectly. Annotations started to be introduced in Java 5.

Three annotations are built-in to the language - Deprecated, SuppressWarnings and Override

@Deprecated

A method is said to be deprecated if it has been replaced by better alternatives. This might be just documented. But using the

@Deprecated annotation means the compiler will also issue a warning if a deprecated method is used.

In NetBeans, the editor will strike-through methods which have this annotation, and if this code is compiled, the output is :

Compiling 1 source file to

C:\Users\walter\Documents\NetBeansProj ects\Test\build\classes

C:\Users\walter\Documents\NetBeansProjects\Test\src\test\Test.java:7: warning:

[deprecation] someMethod() in Base has been deprecated b.someMethod();

1 warning

In NetBeans, this only works if the 'compile on save' option is de-selected.

@SuppressWarnings

This is used to stop compiler warnings of a given type. For example

class Test {

@SuppressWarnings("deprecation")

public static void main(String[] args) { Base b = new Base();

does not generate the warning.

@Override

This signals that a method is intended to override a base class method. For example:

class Base {

void someMethod() { }

}

class SubClass extends Base {

@Override

void someMethod() {

} }

But what is the point? Because this might happen:

We thought we were overriding someMethod, but we spelt it somemethod. Using the @Override annotation means the compiler can detect the error and tell us. This is another example of having a language feature chosen so that the compiler can tell us about errors, rather than us having to figure it out.

In addition to these built-in annotations, it is also possible to define your own, and to write annotation processor tools which will deal with them - such as by inserting source code before compilation.

In document Java (Page 192-200)