Contents |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In C++, a class can be a child of two parents. This concept of multiple inheritance was not adopted in Java because it causes too many difficulties.
Instead, a class can inherit from a parent class and several interface classes. An interface is like a class, except that all methods have no function body. They are, so to speak, only declared abstractly. An interface thus serves only to indicate that a class actually defines certain methods. When a class inherits an interface, it is said to implement the interface. An interface has no properties (variables).
An interface is a promise that certain methods are implemented.
Of course, every class that implements the interface must actually define its methods, otherwise the compiler will report an error.
We will learn about the main application of the interfaces, namely the processing of events, in the next chapter.
In the following example, we use an interface for objects that should have a double value. In the main program, these objects are stored in an array of type Value .
When defining the interface, you only need to abstractly define all the functions you want to use. The function body is omitted. These functions are defined in each class that implements the interface.
/** * Value Interface. Has a getValue() method. */ interface Value { public double getValue (); } /** * Asset Value */ class Asset implements Value { String name; double value; public Asset (String name, double value) { this.name = name; this.value = value; } @Override public double getValue () { return value; } } /** * Account Value */ class Account implements Value { String name; int number; double value; public Account (String name, int number, double value) { this.name = name; this.number = number; this.value = value; } @Override public double getValue () { return value; } } public class InterfaceTest { public static void main (String[] args) { // create two values Value[] values = new Value[2]; values[0] = new Asset("IBM", 21500.0); values[1] = new Account("DB", 67585955, 20000.0); // sum up all values double sum = 0.0; for (Value v : values) sum += v.getValue(); System.out.println("Sum of values : " + sum); } }
As an example, we will write a A quicksort routine that can sort arbitrary objects. It should be noted at the outset that Java already possesses such a routine and its corresponding interface. However, it is instructive to reimplement its functionality.
We've encountered Quicksort before, but back then the sorted data type was double . Now you have a choice:
Java uses the latter solution. First, here is the definition of the interface that all objects to be sorted must implement.
interface SortObject
{
public int compare (SortObject o);
}
As you can see, the function body of `compare` is replaced by a semicolon (;) . The interface is an abstract class.
You can now make any class sortable by implementing `compare`. As an example, we'll again sort double values. To do this, we'll write a new class, `SortDouble`.
class SortDouble implements SortObject
{
private double X;
public SortDouble(double x)
{
X = x;
}
public double getValue ()
{
return X;
}
public int compare (SortObject o)
{
SortDouble s = (SortDouble) o;
if (X < s.X)
return -1;
else if (X == s.X)
return 0;
else
return 1;
}
}
This class can hold a double value and implements the compare method. The main program then follows, which calls a static function `Sorter.sort()`. The `Sorter` class contains a corresponding routine that implements the Quicksort algorithm. This method receives an array of `SortObject` instances as a pass. Since `SortDouble` implements this interface, an array of `SortDouble` will also work.
public class Test
{
public static void main (String args[])
{
int i, n = 1000;
// Get an array of random numbers:
SortDouble x[] = new SortDouble[n];
for (i = 0; i < n; i++)
x[i] = new SortDouble(Math.random());
// Sort it:
Sorter.sort(x);
// Test, if the order is correct:
for (i = 1; i < n; i++)
if (x[i].getValue() < x[i - 1].getValue())
System.out.println("Error sorting");
}
}
The `Sorter.sort` method no longer recognizes double values, but only uses `compare` from the `SortObject` interface. The logic is the same as we used for `double` .
class Sorter
{
static public void sort (SortObject v[])
{
QuickSort(v, 0, v.length - 1);
}
static private void QuickSort (SortObject a[], int lo0, int hi0)
{
int lo = lo0;
int hi = hi0;
SortObject mid;
if (hi0 > lo0)
{
mid = a[(lo0 + hi0) / 2];
while (lo <= hi)
{
while ((lo < hi0) && (a[lo].compare(mid) < 0))
++lo;
while ((hi > lo0) && (a[hi].compare(mid) > 0))
--hi;
if (lo <= hi)
{
swap(a, lo, hi);
++lo;
--hi;
}
}
if (lo0 < hi)
QuickSort(a, lo0, hi);
if (lo < hi0)
QuickSort(a, lo, hi0);
}
}
static private void swap (SortObject a[], int i, int j)
{
SortObject T;
T = a[i];
a[i] = a[j];
a[j] = T;
}
}
As mentioned, Java already includes routines for sorting. The most well-known is Arrays.sort (v), which can be used for any double array or other arrays. In the following program, we use Collections.sort (), which can also sort vectors. However, the vector must consist of objects that implement the Comparable interface.
To avoid type conversion during the comparison, we use the type of Comparable, which can compare with objects of type Name . This simplifies the compareTo function.
It's also interesting to look at the constructor of Name . The parameters have the same names as the properties. To make this work, we have to assign the arguments using this .
import java.util.Collections; import java.util.Vector;
/** * Class that includes a name with first name and last name. * For sorting, we implement the interface Comparable<Name>. */ class Name implements Comparable<Name> { public String firstname, lastname; public Name(String firstname, String lastname) { this.firstname = firstname; this.lastname = lastname; } @Override public int compareTo (Name o) { // If the last names are the same, the first names must be used. // compared. if (lastname.equals(o.lastname)) return firstname.compareTo(o.firstname); return lastname.compareTo(o.lastname); } @Override public String toString () { return lastname + ", " + firstname; } } public class Test { public static void main (String args[]) { Vector<Name> names = new Vector<Name>(); names.add(new Name("Schmidt", "Beate")); names.add(new Name("Schmidt", "Anton")); names.add(new Name("Schmidt", "Xaver")); names.add(new Name("Müller", "Ute")); names.add(new Name("Träger", "Wilhelm")); Collections.sort(names); for (Name name : names) System.out.println(name); } }
It is indeed possible to declare classes abstractly. They are given the keyword `abstract`, as are all methods that do not have a function body.
One example is
abstract class GraphicsObject
{
...
abstract void paint (Graphics g);
...
}
This class contains the abstract `paint` method and must therefore itself be abstract. Only the children of `GraphicsObject` should implement the `paint` method. Therefore, no instance of `GraphicsObject` itself can be created, only its children. Nevertheless, one can, for example, fill a vector with instances of children of `GraphicsObject` and simply use the `paint` method of `GraphicsObject`.
The alternative is to have all children implement an interface called GraphicsObject. However, this is not as logical and also has the disadvantage that interfaces cannot inherit variables.
Program a class that can hold an array of integers and compare such arrays alphabetically. Generate randomly long instances of this class with random contents.