Abstrakte Klassen und Interfaces sind wichtige Konzepte in der objektorientierten Programmierung. In diesem Artikel werden wir uns mit diesen beiden Konzepten näher befassen und ihre Unterschiede kennenlernen.
Abstrakte Klassen
Abstrakte Klassen sind nicht so kompliziert, wie es der Name vermuten lässt. Sie sind eher “sehr allgemeine” Klassen oder das Gegenteil von “konkreten” Klassen.
Ein Beispiel verdeutlicht dies am besten. Stellen wir uns vor, wir möchten ein kleines Tool für geometrische Berechnungen entwickeln. Wir wollen verschiedene geometrische Formen wie Rechtecke und Kreise untersuchen. Es wäre sinnvoll, dafür entsprechende Klassen wie “Rechteck” und “Kreis” zu erstellen und diese von einer gemeinsamen Oberklasse abzuleiten. Die Frage ist nun, welche Oberklasse sich hierfür eignet.
Um diese Frage zu beantworten, überlegen wir uns, welche Gemeinsamkeiten die beiden Unterklassen haben könnten. Es wäre sinnvoll, diese Gemeinsamkeiten dann in der Oberklasse zu implementieren. Für den Moment beschränken wir uns auf zwei Punkte:
- Wir möchten gerne mit Koordinaten angeben, wo sich ein Objekt befindet.
- Wir möchten eine Methode zur Berechnung des Flächeninhalts haben.
Die Koordinaten beziehen sich auf die linke obere Ecke. Bei einem Kreis stellen wir uns vor, dass dieser sich in einem Quadrat befindet und die linke obere Ecke des Quadrats daher die Koordinaten des Kreises darstellt.
Um diese Gemeinsamkeiten umzusetzen, können wir eine abstrakte Klasse mit dem Namen “Figur” verwenden. Diese abstrakte Klasse dient als Rahmen für die Unterklassen “Rechteck” und “Kreis” und stellt sicher, dass diese die Methode für den Flächeninhalt besitzen.
Die abstrakte Klasse “Figur” könnte beispielsweise so aussehen:
public abstract class Figur {
// Attribute
protected double xKoord;
protected double yKoord;
// Konstruktor
public Figur(double pXKoord, double pYKoord){
xKoord = pXKoord;
yKoord = pYKoord;
}
// abstrakte Methode
abstract double flaecheninhalt();
// Ausgabe
public String toString(){
return "(" + xKoord + "|" + yKoord + ")";
}
// get-Methoden
public double getxKoord() {
return xKoord;
}
public double getyKoord() {
return yKoord;
}
}
Die Methode “flaecheninhalt()” ist hier als abstrakt deklariert. Dies bedeutet, dass wir festlegen, dass diese Methode vorhanden sein soll, aber keine Implementierung in der Oberklasse angeben.
Die Klasse “Figur” kann noch nicht direkt instanziiert werden, da sie abstrakt ist. Sie dient jedoch als Vorlage für die Unterklassen “Rechteck” und “Kreis”.
Die Klasse “Rechteck” könnte wie folgt aussehen:
public class Rechteck extends Figur {
// Attribute
private double breite;
private double hoehe;
// Konstruktor
public Rechteck(double pXKoord, double pYKoord, double pBreite, double pHoehe){
super(pXKoord, pYKoord);
breite = pBreite;
hoehe = pHoehe;
}
// Implementierung der abstrakten Methode
public double flaecheninhalt() {
return breite * hoehe;
}
// toString-Methode
public String toString(){
return "Rechteck mit Eckpunkt " + super.toString() + ", Breite " + breite + " und Höhe " + hoehe;
}
}
Die Berechnung des Flächeninhalts für ein Rechteck ist einfach: Breite mal Höhe. Die Methode “flaecheninhalt()” wird in dieser Klasse implementiert.
Die Klasse “Kreis” könnte folgendermaßen aussehen:
public class Kreis extends Figur {
// Attribute
private double radius;
// Konstruktor
public Kreis(double pXKoord, double pYKoord, double pRadius){
super(pXKoord, pYKoord);
radius = pRadius;
}
// Implementierung der abstrakten Methode
public double flaecheninhalt() {
return Math.PI * radius * radius;
}
// toString-Methode
public String toString(){
return "Kreis mit Radius " + radius;
}
}
Die Berechnung des Flächeninhalts für einen Kreis erfolgt durch das Quadrat des Radius multipliziert mit Pi. Auch hier wird die abstrakte Methode “flaecheninhalt()” implementiert.
Diese Klassen können nun getestet werden:
public class Figurtest {
public static void main(String[] args) {
Rechteck meinRechteck = new Rechteck(100, 100, 40, 20);
System.out.println(meinRechteck);
System.out.println(meinRechteck.flaecheninhalt());
Kreis meinKreis = new Kreis(400, 200, 10);
System.out.println(meinKreis);
System.out.println(meinKreis.flaecheninhalt());
}
}
Die Ausgabe sieht wie folgt aus:
Rechteck mit Eckpunkt (100.0|100.0), Breite 40.0 und Höhe 20.0
800.0
Kreis mit Radius 10.0
314.1592653589793
Interfaces / Schnittstellen
Interfaces sind eine Erweiterung von abstrakten Klassen. Sie bestehen nur aus abstrakten Methoden, im Gegensatz zu abstrakten Klassen, die auch einige implementierte Methoden haben können.
Ein Interface kann als Vertrag betrachtet werden, der von den implementierenden Klassen eingehalten werden muss. Es spezifiziert, welche Methoden die Klasse implementieren muss, aber nicht, wie die Methoden implementiert werden sollen.
Um den Unterschied zwischen abstrakten Klassen und Interfaces zu verdeutlichen, betrachten wir ein weiteres Beispiel. Hier wird ein Interface namens “Flaeche” eingeführt, das die abstrakte Methode “flaecheninhalt()” vorgibt.
Das aktualisierte Klassendiagramm sieht wie folgt aus:
Die abstrakte Klasse “Figur” hat nicht mehr die abstrakte Methode “flaecheninhalt()”. Stattdessen wurde das Interface “Flaeche” eingeführt, das diese Methode vorgibt. Die Klassen “Rechteck” und “Kreis” implementieren dieses Interface.
Das Interface “Flaeche” kann wie folgt aussehen:
public interface Flaeche {
double flaecheninhalt();
}
In einem Interface sind alle Methoden automatisch abstrakt und müssen nicht als solche deklariert werden.
Die abstrakte Klasse “Figur” muss nun das Interface “Flaeche” implementieren:
public abstract class Figur implements Flaeche {
// Attribute, Konstruktor, abstrakte Methode, toString-Methode, get-Methoden
}
Die Klassen “Rechteck” und “Kreis” werden wie zuvor angepasst und implementieren das Interface “Flaeche”:
public class Rechteck extends Figur implements Flaeche {
// Attribute, Konstruktor, Implementierung der abstrakten Methode
}
public class Kreis extends Figur implements Flaeche {
// Attribute, Konstruktor, Implementierung der abstrakten Methode
}
Interfaces ermöglichen es uns, abstrakte Methoden zu definieren und gleichzeitig Mehrfachvererbung zu vermeiden.
Fazit
Abstrakte Klassen und Interfaces sind leistungsstarke Werkzeuge in der objektorientierten Programmierung. Sie ermöglichen es uns, Code zu abstrahieren, Gemeinsamkeiten zu definieren und die Struktur und den Vertrag von Klassen festzulegen. Abstrakte Klassen werden verwendet, wenn wir eine teilweise Implementierung bereitstellen möchten, während Interfaces verwendet werden, um die Implementierung vollständig den implementierenden Klassen zu überlassen.