When and why should abstract classes be used? I would like to see some practical examples of their uses. Also, what is the difference between abstract classes and interfaces?
http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html
http://java.sun.com/docs/books/tutorial/java/concepts/interface.html
In short, an abstract class can be partially implemented, an interface cannot. More details in the links above.
An interface contains no implementation at all.
An abstract class may contain some implementation, that is useful to all subclasses, but not be complete : it needs to be completed in some way in the subclasses.
Where the interface lets you use polymorphism on several classes, abstract class also lets them reuse code.
public abstract class Figure {
protected Point position;
public abstract void draw();
}
public class Square extends Figure {
// position is usable
public void draw() {
// this method must be implemented, for Square not to be abstract
}
// here is other code
}
Another difference is that a class can only have one superclass, but implements many interfaces. This can be a limiting factor.
Abstract classes are "half-implementations" of a class. They can be partially implemented with some generic functionality, but leave part of the implementation to the inheriting classes. You could have an abstract class called Animal
that has implemented some generic behavior/values such as Age
, Name
, SetAge(...)
. You can also have methods that are not implemented (they are abstract
), much like an interface.
Interfaces are simply contracts that specify behaviors that should be available for a class. You could have an interface such as IWalker
that requires public method Walk()
, but no specifics on how it is implemented.
Classes that are entirely abstract (all methods are abstract) are (almost) the same as interfaces (the major difference being they can contain fields and non-public abstract methods, which interfaces cannot). The difference is when you have an abstract class which contains a method which has some common functionality which will be the same for all derived children.
If you want to model a Filesystem, for example, you know that, regardless of the object type, you will have a path for an item. You'd want to have a common implementation for getting that path (no point in writing the same thing over and over again), and leave anything special for the children to implement.
As KLE correctly explained, the main difference between interface and abstract class is that an abstract class may contain fields and method bodies, while an interface may only contain method signatures (and constants, i.e. public static final fields).
Another important distinction is that a class can implement multiple interfaces, but it can only (directly) inherit from one class (abstract or not). So for things which people will probably use in addition to other functionality, an interface makes more sense than an abstract class. See e.g. the interfaces Comparable in the JDK.
As an example:
In the system we develop, we have a class for starting a data import. We have many different kinds of data imports, but most have some things in common: They read data from a file, they write it to the database, they produce an import protocol etc.
So we have an abstract class "Import", which contains implemented methods for things like writing protocol entries, finding all files to import, deleting processed import files etc. The specifics will be different for each import, so there are abstract methods that serve as extension hooks, e.g. getFilenamePattern() which is used by the reading method to find the files that can be imported. getFilenamePattern is implemented in the concrete subclass, depending on what kinds of files need to be imported.
That way, the shared import functionality is in one place, while the specifics for one kind of import are separate.
If you need to wrap your head around the concept of abstract classes, take a look at the Swing UI toolkit (or at AWT) in the standard library.
Because you can imagine what can be visualized (e.g., a button, a label), it's easy to contrast it with the things that can't be instantiated (e.g., a JComponent).
You can restrict the order of execution of an instruction with specific steps, but allow delegation for the behavior of each step:
public abstract class Instruction {
void perform() {
firstStep();
secondStep();
thirdStep();
}
abstract void firstStep();
abstract void secondStep();
abstract void thirdStep();
}
Surprisingly, many examples/explanations given here do not provide good arguments for using an abstract class. Merely putting common fields/methods in a superclass does not require it to be abstract. Also (start rant), shame on supposedly knowledgeable engineers still coming up with Animal / Vehicle / Figure hierarchies to 'explain' object oriented concepts. These types of examples are very misleading because they point you in the wrong direction; you generally should NOT favour straight subclassing because it creates a very tight coupling between the classes. Rather use collaboration (rant ends).
So what do I think is a good use case for an abstract class? One of my favorite examples is an application of the 'template method' GoF pattern. Here you want to specify the generic flow of an algorithm once, but allow multiple implementations of the individual steps. Here an example I just put together of a VirusScanEngine containing the main virus scanning algorithm (find the next virus, either delete or report it, continue until scan is complete), and a LinearVirusScanner which implements the required algorithm steps (findVirus, deleteVirus and reportVirus). My apologies to all developers really working on virus scanning software for this horrendous simplification.
import java.util.Arrays;
public abstract class VirusScanEngine {
public static void main(String[] args) {
byte[] memory = new byte[] { 'a', 'b', 'c', 'M', 'e', 'l', 'i', 's', 's',
'a' , 'd', 'e', 'f', 'g'};
System.out.println("Before: " + Arrays.toString(memory));
new LinearVirusScanner().scan(memory, Action.DELETE);
System.out.println("After: " + Arrays.toString(memory));
}
public enum Action {
DELETE, REPORT
};
public boolean scan(byte[] memory, Action action) {
boolean virusFound = false;
int index = 0;
while (index < memory.length) {
int size = findVirus(memory, index);
if (size > 0) {
switch (action) {
case DELETE:
deleteVirus(memory, index, size);
break;
case REPORT:
reportVirus(memory, index, size);
break;
}
index += size;
}
index++;
}
return virusFound;
}
abstract int findVirus(byte[] memory, int startIndex);
abstract void reportVirus(byte[] memory, int startIndex, int size);
abstract void deleteVirus(byte[] memory, int startIndex, int size);
}
and
public class LinearVirusScanner extends VirusScanEngine {
private static final byte[][] virusSignatures = new byte[][] {
new byte[] { 'I', 'L', 'O', 'V', 'E', 'Y', 'O', 'U' },
new byte[] { 'M', 'e', 'l', 'i', 's', 's', 'a' } };
@Override
int findVirus(byte[] memory, int startIndex) {
int size = 0;
signatures: for (int v = 0; v < virusSignatures.length; v++) {
scan: {
for (int t = 0; t < virusSignatures[v].length; t++) {
if (memory[startIndex + t] != virusSignatures[v][t]) {
break scan;
}
}
// virus found
size = virusSignatures[v].length;
break signatures;
}
}
return size;
}
@Override
void deleteVirus(byte[] memory, int startIndex, int size) {
for (int n = startIndex; n < startIndex + size - 1; n++) {
memory[n] = 0;
}
}
@Override
void reportVirus(byte[] memory, int startIndex, int size) {
System.out.println("Virus found at position " + startIndex
+ " with length " + size);
}
}