- abstrakte Klassen
Abstrakte Klassen in Java
Durch Erweiterung bestehender Basisklassen können für spezielle Zwecke angepaßte Klassen erzeugt werden. Es kann sein, daß eine Klasse nicht mit der Absicht geschrieben wurde, Typ von Exemplaren zu sein, sondern damit sie als Basisklasse für Erweiterungen dient.
Eine Klasse kann mit dem Klassenmodifizierer "abstract" deklariert werden. Eine abstrakte Klassen kann keine Exemplare haben, aber sie kann als Basisklasse verwendet werden.
AbstractClass.java
public final class AbstractClass
{ public static void main( String args[] )
{ new A(); }}
abstract class A {}Konsole
AbstractClass.java:3: A is abstract; cannot be instantiated
{ new A(); }}
^AbstractClass1.java
public final class AbstractClass1
{ public static void main( String args[] ){ new B(); }}
abstract class A {} class B extends A {}Konsole
Abstrakte Klassen dienen als Grundlage einer Erweiterung, aber nicht als Typ von Exemplaren.
Abstrakte Methoden
Durch abstrakte Methoden kann eine abstrakte Klasse Anforderungen an ihre Erweiterungen stellen: Alle nicht-abstrakten Erweiterungen müssen alle abstrakten Methoden mit einer nicht-abstrakten Methodendeklaration ersetzen.
Eine abstrakte Methodedeklaration sieht aus wie eine Signatur ohne Methodenrumpf. An der Stelle, an der bei einer Methodendeklaration der Methodenrumpf ist, befindet sich ein Semikolon ";". Da ohne Methodenrumpf nicht festgelegt ist, wie eine Signatur implementiert ist, kann die abstrakte Methode natürlich so nicht aktiviert werden. Selbst, wenn es erlaubt wäre, daß Exemplare von abstrakten Klassen erzeugt werden, könnte ein Exemplar dann nicht mehr erzeugt werden, wenn die abstrakte Klasse eine abstrakte Methode enthält, weil das Objekt dann keine Methode hätte, um auf eine entsprechende Nachricht zu reagieren. Daher dürfen abstrakte Methoden auch nur in abstrakten Klassen deklariert werden.
Es ist möglich, daß eine abstrakte Methode einer abstrakten Klasse in einer nichtabstrakten Erweiterung durch eine nichtabstrakte Methodendeklaration mit der gleichen Signatur ersetzen und dadurch implementiert wird.
AbstractMethod.java
public final class AbstractMethod
{ public static void main( final java.lang.String[] args )
{ final German german = new German ();
final English english = new English();
java.lang.System.out.println( german .hello() );
java.lang.System.out.println( english.hello() ); }}
abstract class Language { abstract String hello(); }
final class German extends Language { String hello(){ return "Hallo"; }}
final class English extends Language { String hello(){ return "hello"; }}System.out
Hallo
hello
In UML werden abstrakte Klassen und abstrakte Methoden kursiv geschrieben. In Textmedien, die Kursivschrift nicht zulassen, wird diese gelegentlich dadurch gekennzeichnet, daß der kursiv zu schreibende Text zwischen Schrägstriche geschrieben wird, was natürlich zu Mißverständnissen führen kann, wenn diese Schrägstriche auch anders interpretiert werden könnten.
Language [UML class diagram]
.----------------------.
| «abstract class» |
| /Language/ |
|----------------------|
|----------------------|
| /hello() : String/ |
'----------------------'
^
/_\
«extends» |
|
|
.---------------------------------------+
| |
.-----------------------. .-----------------------.
| «class» | | «class» |
| German | | English |
|-----------------------| |-----------------------|
|-----------------------| |-----------------------|
| hello() : String | | hello() : String |
'-----------------------' '-----------------------'
Das Schablonenmethode-Muster
Eine abstrakte Methode kann in Methoden ihrer abstrakten Klasse angewendet werden, auch wenn ihr Rumpf in der abstrakten Klasse nicht angegeben wird. Wenn dies in sinnvoller Weise geschieht, so wendet man das Schablonenmethode-Muster (template method pattern ) an. Hierbei handelt es sich nicht um einen Begriff der Programmiersprache Java, sondern um eine bei der objektorientierten Programmierung allgemein anwendbare Vorgehensweise.
Wenn die abstrakte Klasse "Language" nur als Basisklasse für Sprachen gedacht ist, in denen der Name "Peter" mit der Zeichenfolge "Peter" geschrieben wird und ein Hallo-Gruß einem Namen vorangestellt werden kann, dann ist die Kette "hello() + " Peter"" eine allgemeine Begrüßung von Peter, in der die Schreibweise des Hallo-Grußes noch offengelassen wurde. Diese wird dann erst durch die tatsächlich verwendete nichtabstrakte Erweiterungs-Klasse festgelegt und dann in der Schablonenmethode entsprechend verwendet.
TemplateMethodPattern.java
public final class TemplateMethodPattern
{ public static void main( final java.lang.String[] args )
{ final German german = new German ();
final English english = new English();
java.lang.System.out.println( german .helloPeter() );
java.lang.System.out.println( english.helloPeter() ); }}
abstract class Language { abstract String hello();
String helloPeter(){ return this.hello() + " Peter"; }}
final class German extends Language { String hello(){ return "Hallo"; }}
final class English extends Language { String hello(){ return "hello"; }}System.out
Hallo Peter
hello Peter
Die nichtabstrakte Methode "helloPeter" ist die Schablonenmethode (template method ). Die abstrakte Methode "hello" wird in diesem Zusammenhang auch als Hakenmethode (hook method ) bezeichnet. Eine Schablonenmethode kann auch mehrere Hakenmethoden verwenden.
Language [UML class diagram]
.-----------------------.
| «abstract class» |
| /Language/ |
|-----------------------|
|-----------------------|
| /hello() : String/ |
| helloPeter() : String |
'-----------------------'
^
/_\
«extends» |
|
|
.---------------------------------------+
| |
.-----------------------. .-----------------------.
| «class» | | «class» |
| German | | English |
|-----------------------| |-----------------------|
|-----------------------| |-----------------------|
| hello() : String | | hello() : String |
'-----------------------' '-----------------------'Template Method Pattern
.-----------------------.
| «abstract class» |
| /<AbstractClass>/ |
|-----------------------|
|-----------------------|
| /<hookMethod>/ |
| <templateMethod> |
'-----------------------'
^
/_\
«extends» |
|
|
|
.-----------------------.
| «class» |
| <SubClass> |
|-----------------------|
|-----------------------|
| <hookMethod> |
'-----------------------'
In einigen Programmiersprachen, wie C++, gibt es Schablonen (templates ) und mit ihrer Hilfe definierte Methoden könnten ebenfalls als „Schablonenmethoden“ bezeichnet werden. Dieser Begriff ist aber ein anderer Begriff als der Begriff „Schablonenmethode“ im Sinne des hier vorgestellten Schablonenmethode-Musters. Was in einem bestimmten Fall mit „Schablonenmethode“ gemeint ist, muß aus dem Zusammenhang entnommen werden.
Bei Verwendung des Schablonenmethode-Musters wird das Gerüst eines Verfahrens vorgegeben, während Details zunächst offengelassen werden, um später durch Methodendeklarationen in Unterklassen ausgefüllt zu werden.
Das Schablonenmethode-Muster erlaubt es Unterklassen, bestimmte Teile eines Verfahrens zu definieren, ohne daß Änderungen an der Methode nötig werden, die das Verfahren implementiert, und ist somit auch eine Umsetzung des Offen-Geschlossen-Prinzips unter Verwendung spezieller Eigenschaften der Klassen-Erweiterung und des Ersetzens.
Dynamische Bindung, Polymorphie
Das Schablonenmethode-Muster kann so nur deswegen verwendet werden, weil die Zuordnung einer Nachricht zu einer Methode in Java erst zur Laufzeit anhand des momentanen Typs des Exemplars erfolgt. Daher bewirkt die Nachricht "hello()" in der Anweisung "return this.hello() + " Peter";" einmal die Aktivierung der Methode "German#hello()" und einmal die Aktivierung der Methode "Englisch#hello()"—je nach dem Typ des Objektes "this". Hier wird wieder Polymorphie verwendet, also die Versendung einer Nachricht an einen Empfänger, dessen Typ durch den Versendungsausdruck nicht ganz festgelegt ist.
Nichtabstrakte Basisklassen
Die hier beschriebenen Verfahren und auch die Anwendungen des Schablonenmethoden-Muster benötigen nicht unbedingt eine abstrakte Basisklasse oder abstrakte Methoden, denn nichtabstrakte Methode einer nichtabstrakten Klasse können genausogut ersetzt werden.
Die Verwendung einer abstrakte Klasse erlaubt es nur auszudrücken, daß diese Klasse für Erweiterungen und nicht für Exemplare gedacht ist. Eine abstrakte Methode macht es nicht nötig, irgendeinen Methodenrumpf als Platzhalter zu verwenden, der gar nicht aktiviert werden soll, und sie macht es auch deutlich, daß diese Signatur ersetzt werden soll.
Statische Methoden
Das hier beschriebene Verfahren kann für statische Methoden nicht angewendet werden. Es ist gar nicht erlaubt, eine statische Methode abstrakt zu deklarieren. Nun könnten auch an die Verwendung nichtabstrakter statischer Methoden gedacht werden, da diese in Erweiterungen verdeckt werden können. Doch die Verdeckung durch eine Erweiterung ist kein Ersetzen, denn die Nachrichten zu statischen Methoden werden auch statisch aufgelöst: Polymorphie funktioniert aber so nicht und das Schablonenmethode-Muster kann daher auch nicht mit statischen Methoden umgesetzt werden.
Weil statische Methoden verschiedene andere Techniken ebenfalls nicht erlauben, sollten sie möglichst vermieden werden.
abstrakte und finale Klassen im Vergleich
Eine abstrakte Klasse ist in gewissem Sinne das Gegenstück zu einer finalen Klasse : Eine abstrakte Klasse kann keine Exemplare haben und ist nur dafür gedacht, erweitert zu werden; eine finale Klasse kann keine Erweiterungen haben und ist nur dafür gedacht, Exemplare zu haben.