Thema in Kurzform
Von abstrakten Klassen können keine Objekte erzeugt werden.
Abstrakte Methoden haben keinen Rumpf und dürfen nur in abstrakten Klassen vorkommen. Sie müssen in einer Unterklasse überschrieben und implementiert werden.
Um den Sinn hinter dem Konzept der abstrakten Klassen zu verstehen, schauen wir uns zunächst einmal folgende einfache Vererbungshierarchie an:
Es ist naheliegend, dass wir von den Klassen Falke, Elefant und Eisbaer Objekte erzeugen. Wie sinnvoll ist es aber, die allgemeine Klasse Tier zu instantiieren? Wie können uns ganz konkret einen Falken, einen Elefanten oder einen Eisbären vorstellen. Aber ein allgemeines "Tier"? Wohl eher nicht.
Tatsächlich ist es so, dass die Klasse Tier viel zu unspezifisch ist, als dass wir davon Objekte erzeugen sollten. Denn wir wollen keine Tier-Objekte, sondern Falke-, Elefant- und Eisbaer-Objekte!
Das heißt natürlich nicht, dass die Klasse Tier unwichtig wäre. Ganz im Gegenteil: Sie ist absolut essentiell für unsere Vererbungslogik und Programmstruktur, zumal sie als Superklasse für andere Klassen dient.
Tier ist etwas Abstraktes, von dem es keine Objekte geben sollte. Um zu verhindern, dass eine Klasse wie Tier jemals instantiiert wird, müssen wir sie als abstract markieren:
public abstract class Tier {
//
}
Setzen wir also das Schlüsselwort abstract direkt hinter den Zugriffsmodifikator, haben wir die Klasse als abstrakte Klasse markiert, von der niemals(!) ein Objekt erstellt werden kann. Versuchen wir es dennoch, erhalten wir einen Compiler-Fehler.
Tier t = new Tier(); // nein!
Der Compiler meldet: 'Tier' is abstract; cannot be instantiated. Genau so muss das sein 😀
Klassen, die nicht abstrakt sind, nennt man konkrete Klassen. Davon können Objekte erzeugt werden.
Wenn wir in unseren Programmen die Vererbungsstruktur anlegen, sollten wir genau überlegen, welche Klassen abstrakt und welche konkret sind. Konkrete Klassen sollten so spezifisch wie möglich sein, dass die Objekterzeugung sinnvoll erscheint.
Erweitern wir unsere Vererbungshierarchie von oben nun ein wenig, um das Zusammenspiel von abstrakten und konkreten Klassen zu sehen.
Die Klasse Falke war zuerst noch eine konkrete Klasse. Da wir sie aber hier um zwei Unterklassen erweitert haben (Sakerfalke und Turmfalke) sind diese beiden spezifischer, d.h. konkreter, als Falke. Deshalb sollten wir Falke als abstractmarkieren und die Objekterzeugung von dieser Klasse verbieten. Falke ist damit eine abstrakte Unterklasse. Dasselbe Prinzip gilt auch bei den Klassen Baer, Grizzly und Eisbaer. Elefant wird nicht erweitert, auch wenn das natürlich ohne Weiteres denkbar wäre.
Neben Klassen können auch Methoden als abstrakt markiert werden:
public abstract class Tier {
public abstract void bewegen();
}
In der abstrakten Klasse Tier haben wir eine abstrakte Methode bewegen() definiert. Dabei wird Schlüsselwort abstract direkt hinter den Zugriffsmodifikator gesetzt. Fällt Ihnen hier aber noch etwas anderes auf?
Richtig: Es fehlen die beiden geschweiften Methoden-Klammern { } und die Methode hat keinen Rumpf. Daraus ergeben sich folgende Regeln für abstrakte Methoden:
Würden wir abstrakte Methoden in konkreten Klassen zulassen, hieße das, dass wir Objekte mit nicht implementierten Methoden erzeugen könnten. Was für eine grausame Vorstellung 😨
Beim Entwickeln von objektorientierten Programmen wissen wir schon im oberen Bereich des Vererbungsbaums, dass wir bestimmte Methoden für konkrete Unterklassen brauchen werden. Allerdings wäre es noch zu früh und die Oberklasse ist zu unspezifisch, um die konkrete Funktionsweise dieser Methoden für alle Unterklassen jetzt schon festzulegen. Diese Arbeit wird eine geeignete Unterklasse erledigen.
Denken Sie doch mal an die abstrakte Klasse Tier. Wir wissen, dass ein Tier sich bewegen kann (darum hat die Klasse auch eine Methode bewegen()). Allerdings ist Tier noch viel zu allgemein und vage, als dass wir wüssten, wie diese Bewegung genau sein soll. Es ist unmöglich, dass sich alle Tierarten gleich bewegen! Ein Falke etwa bewegt sich vollkommen anders als ein Elefant oder Bär. Eben deshalb hat die Methode zu diesem Zeitpunkt noch keinen Code und ist abstrakt.
Wie gesagt muss garantiert sein, dass eine abstrakte Methode von einer der Unterklassen implementiert und damit konkret wird. So können wir bewegen() etwa in der Klasse Elefant überschreiben und implementieren:
@Override
public void bewegen(){
System.out.println("Ich bewege mich schwer und oft langsam");
}
Elefant ist eine konkrete Klasse, da sie die von Tier geerbte abstrakte Methode bewegen() implementiert, selbst keine abstrakte Methode hat sowie nicht als abstrakte Klasse markiert wurde.
Wie sieht es nun aber mit Falke aus? In unserer Grafik oben ist die Klasse als abstract gekennzeichnet. Dies kann drei Ursachen haben:
Das Gleiche gilt natürlich auch für abstrakte Klasse Baer.
Soviel zu den abstrakten Klassen und Methoden. Wir hoffen, wir konnten das Thema etwas klarer machen.
Java Basics
[Java einrichten] [Variablen] [Primitive Datentypen] [Operatoren] [if else] [switch-case] [Arrays] [Schleifen]
Objektorientierung
[Einstieg] [Variablen ] [Konstruktor] [Methoden] [Rekursion] [Statische Member] [Initializer] [Pass-by-value] [Objektsammlungen] [Objektinteraktion] [Objekte löschen]
Klassenbibliothek
[Allgemeines] [String ] [Math] [Wrapper] [Scanner] [java.util.Arrays] [Date-Time-API]
Vererbung
[Einstieg Vererbung] [Konstruktoren bei Vererbung ] [Der protected Zugriffsmodifikator] [Abstrakte Klassen und Methoden] [Polymorphie in Java] [Typumwandlung] [Die Klasse Object] [Die toString()-Methode] [Objekte vergleichen] [Was ist ein Interface?]