I use this blog as a soap box to preach (ahem... to talk :-) about subjects that interest me.

Friday, September 3, 2010

OO basics and Java

In relative terms, some classes are very general, others very specialised. We use the term subclass to indicate a class which is a more specialised version of another class. For example, the class Convertible is a subclass of the class Car, because a convertible is a special type of car. The reverse is obviously not true, because not all cars have a retracting or folding roof. It would be incorrect to say for example that the class Bike is a subclass of Car, although both are subclasses of Vehicle.

The term superclass is used to indicate the more general class when talking about one of its subclasses.

A subclass inherits all the attributes and methods of its superclass and adds some more of its own. This means that when dealing with a subclass, we know that it has all the attributes and methods of its superclass, while the reverse is not true. This makes a lot of sense. For example, the method openTop and closeTop would be appropriate for a convertible, but not for a generic car. On the other hand, a convertible is just a particular type of car. Therefore, you expect to be able to do with it what is generally possible with any car.

Classes can be organised into an inheritance tree, with the most generic class as root and the most specialised as leaves.

There are also cases where a class has more than one superclass. For example, a class myCar will probably inherit from both Car and MyProperties. This is called multiple class inheritance. Java only supports single class inheritance, but allows classes to inherit from multiple Java interfaces.

As I said, it makes sense when describing or defining a class to concentrate on attributes and methods that are relevant to the environment in which we intend to use it. In addition to that, we also have to ensure that only the necessary information is made available outside the class. In other words, we have to conceal the details of how the class works. This technique is called encapsulation.

By encapsulating the working details of a class, we achieve two important results: first of all, we don't clutter the class interface with useless or confusing information; secondly, we maximise the freedom we have when implementing the class using a computer language (e.g., Java).

For example, a car buyer is interested in external attributes like colour and fuel consumption. If we were designing a Car class for an online car sales application, it wouldn't make any sense to include attributes for things like the brand of cabling and the size of bolts and nuts. Obviously, it would be a different matter if we were designing a program for selling spare parts.

Polymorphism indicates the ability of methods to behave differently depending on the conditions under which they are invoked. In practical terms, from a programming point of view, polymorphism means that you can execute the same method for different types of objects and with different types of parameters.

For example, in Java the most generic class (the superclass of all classes, root of the class inheritance hierarchy) is java.lang.Object. The method toString available for Object is therefore inherited by all classes. The consequence of this is that if you have a variable myObj referring to an object of any type, you can always write myObj.toString() regardless of the class the object instantiates. Most classes override (i.e., replace) the inherited toString method with a specific one, but you don't need to worry about that.

If you look at the class definition of java.lang.Integer, you discover that the method toString has two additional formats in addition to the generic one:

static String toString(int i)
static String toString(int i, int radix)

This is another example of polymorphism.

The same concepts apply to operators. A typical example is Java's plus operator ('+'), which adds numbers but, when applied to strings, concatenates them. This mechanism is referred to as operator overloading.

Abstract Classes & Java Interfaces
In programs, like in real life, we normally deal with objects. Classes are abstractions of object properties and behaviours and we need to instantiate them in order to have something to work with. And yet, in many cases, classes are not abstract enough and we need to define abstract classes.

To understand what this means, consider once more our Car class. Even if we included in its definition all the generic features we can possibly imagine, would it make sense to instantiate such a class? Not really. Each car manufacturer invents special features not found in cars made by their competitors. Without those features, you cannot describe a real car, and yet we cannot include them in our generic Car class, precisely because they are specific to each make.

The solution is to make Car a class prototype, with methods to be found in all cars, but not instantiable to an object. This is what an abstract class is. In Java, abstract classes can include a mix of methods and abstract methods. To define a class that can be instantiated, you must override all abstract methods, but can decide to keep some or all of the non-abstract ones.

Beside abstract classes, Java also supports the definition of interfaces. An interface is an abstract type that can contain method signatures and constants. A class can then implement more than one interface.

Interfaces and abstract classes make possible to define in Java class shells that provide default constants and methods.

For example, the interface java.lang.Iterable provides the method iterator; the interface java.util.Collection extends java.lang.Iterable with 14 additional methods; and the abstract class java.util.AbstractCollection implements the java.util.Collection interface. If you want to implement a collection class, you only need to extend java.util.AbstractCollection and implement its two abstract methods iterator and size. All the rest is already available by default.

No comments:

Post a Comment