Polymorphie
Ersetzen
Welche Ausgabe erzeugt das folgende Programm?
Main.java
class Zimmer
{ public java.lang.String asString()
{ return "Kobold"; }}class Tapete
{ public java.lang.String asString()
{ return "Zirkus"; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( new Tapete().asString() );
java.lang.System.out.println( new Zimmer().asString() ); }}
Das folgende Programm ersetzt die Methode »toString()« aus der Basisklasse »java.lang.Objekt«.
Main.java
final class Stunde
{ public java.lang.String toString()
{ return "Patent"; }}final class Mammut
{ public java.lang.String toString()
{ return "Löffel"; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( new Stunde().toString() );
java.lang.System.out.println( new Mammut().toString() ); }}java.lang.System.out
Patent
Löffel
Die Absicht des Ersetzens wird mit »@java.lang.Override« (Eine „Annotation“, also Kennzeichnung, einer Deklaration.) verdeutlicht.
Main.java
final class Stunde
{ @java.lang.Override public java.lang.String toString()
{ return "Patent"; }}final class Mammut
{ @java.lang.Override public java.lang.String toString()
{ return "Löffel"; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( new Stunde().toString() );
java.lang.System.out.println( new Mammut().toString() ); }}java.lang.System.out
Patent
Löffel
In dem folgenden Programm fehlt der Aufruf von ».toString()«. Warum geht es trotzdem?
Main.java
final class Stunde
{ @java.lang.Override public java.lang.String toString()
{ return "Patent"; }}final class Mammut
{ @java.lang.Override public java.lang.String toString()
{ return "Löffel"; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( new Stunde() );
java.lang.System.out.println( new Mammut() ); }}java.lang.System.out
Patent
Löffel
Obiges Beispiel zeigt einen versteckten Aufruf einer Methode. Hat man davon keine Ahnung, würde man nicht denken, daß die Methode »toString()« irgendwo im obigen Programm aufgerufen wird!
Wir haben eine Art von Methodenreferenz an »println(java.lang.String)« übergeben! Also praktisch eine Methode (verpackt in einem Objekt). Je nach der übergebenen Methode ändert sich die Ausgabe. Dies ist eine der wichtigsten Anwendungen von Objekten: Sie erlauben es, zur Laufzeit Methoden durch Wert von Ausdrücken zu repräsentieren.
Main.java
final class Stunde
{ @java.lang.Override public java.lang.String toString() /* Methode 0 */
{ return "Patent"; }}final class Mammut
{ @java.lang.Override public java.lang.String toString() /* Methode 1 */
{ return "Löffel"; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.Object object;
object = new Stunde(); java.lang.System.out.println( object );
object = new Mammut(); java.lang.System.out.println( object ); }}java.lang.System.out
Patent
Löffel
Das voranstehende Programm zeigt schon eine Art von Polymorphie, indem derselbe Ausdruck (»object«) für Objekte verschiedener Klassen steht (das ist die Bedeutung von „polymorph“, der Ausdruck »object« ist polymorph, da er für Objekte verschiedener Klassen steht).
Noch deutlicher wird dies in dem folgenden Programm, wo der polymorphe Ausdruck nur einmal im Quelltext vorkommt.
Main.java
final class Stunde
{ @java.lang.Override public java.lang.String toString() /* Methode 0 */
{ return "Patent"; }}final class Mammut
{ @java.lang.Override public java.lang.String toString() /* Methode 1 */
{ return "Löffel"; }}public final class Main
{public static void execute( final java.lang.Object object )
{ java.lang.System.out.println
( ":" + object.toString() /* <- Polymorphie! */ + ":" ); }public static void main( final java.lang.String[] args )
{ execute( new Stunde() );
execute( new Mammut() );
execute( new java.lang.Object() );
execute( new java.lang.String() ); }}java.lang.System.out
:Patent:
:Löffel:
:java.lang.Object@d86c58:
::
Der entscheidende Vorteil der objektorientierten Programmierung ist die Möglichkeit zur Erweiterung von Signaturen (wie »toString()«) für neue Typen, ohne daß dazu vorhandene Typen verändert werden müssen.
Das folgende Beispielprogramm erweitert die Bedeutung der Signatur »toString()« für die Klasse »Kultur«, während diese Signatur für Standardklassen, wie beispielsweise die Klasse »java.lang.Object« schon definiert ist.
Main.java
final class Kultur
{ @java.lang.Override public java.lang.String toString()
{ return "Kosmos"; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( new java.lang.Object() );
java.lang.System.out.println( new Kultur() ); }}java.lang.System.out
java.lang.Object@1cda81e
Kosmos
Die oben gezeigte Technik verlangt aber das Ersetzen einer Signatur in einer Klasse aus der Standardbibliothek. Diese Klasse konnte dann als Typ des polymorphen Parameters in »execute( final java.lang.Object object )« verwendet werden. Was aber, wenn keine dafür geeignete Basisklasse in der Standardbibliothek gefunden werden kann?
In solch einem Fall kann auch eine Schnittstelle herangezogen werden.
Datenaufrufe
Das folgende Programm zeigt erneut, wie Objekte den Aspekt des Datums mit dem des Programms vereinigen: Die Methode »execute« erhält ein Objekt zunächst als „passives Datum“ übergeben. Dann wird dieses Objekt jedoch durch Aufruf seiner Methode »wait« aktiviert und agiert aktiv wie ein Programm.
Main.java
final class Alpha
{ public int run(){ java.lang.System.out.println( "alpha" ); return 0; }}
final class Beta
{ public int run(){ java.lang.System.out.println( "beta" ); return 0; }}
final class Other
{ public static void execute( final Object object /* "Datum" oder "Programm"? */ )
{ object.run(); }}
public final class Main
{ public static void main( final java.lang.String[] args )
{ Other.execute( java.lang.Math.random() > 0.5 ? new Alpha() : new Beta() );
Other.execute( java.lang.Math.random() > 0.5 ? new Alpha() : new Beta() ); }}
Statische Felder
(Erst Nachbesprechung der vorigen Aufgabe!) Erweitern Sie die Deklaration der folgenden Klasse »Object« so, daß der weiter unten schon vorhanden Aufruf »Object.count()« vom Datentyp »int« ist und angibt, wie viele Objekt dieser Klasse »Object« erzeugt wurden (3 ist nur ein Beispiel, es können auch mehr oder weniger sein).
Main.java
final class Object
{ … }public final class Main
{ public static void main( final java.lang.String[] args )
{ new Object(); new Object(); new Object(); /* 3 Objekte */
java.lang.System.out.println( Object.count() ); }}java.lang.System.out
3
Polymorphie von Namen
Im Gegensatz zu dem früher behandelten Fall der Überladung mit verschiedenen Parametertypen, erfolgt die Auflösung im folgenden Fall nach dem Objekttyp, nicht nach dem Typ des Ausdrucks.
Main.java
final class ProgramA implements java.lang.Runnable
{ public void run(){ java.lang.System.out.println( "A" ); }}
final class ProgramB implements java.lang.Runnable
{ public void run(){ java.lang.System.out.println( "B" ); }}
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.Runnable runnable;
runnable = java.lang.Math.random() > 0.5 ? new ProgramA(): new ProgramB();
runnable.run()/* späte Bindung, Polymorphie! */; }}
Polymorphie von Parametern
Main.java
final class ProgramA implements java.lang.Runnable
{ public void run(){ java.lang.System.out.println( "A" ); }}
final class ProgramB implements java.lang.Runnable
{ public void run(){ java.lang.System.out.println( "B" ); }}
final class Computer
{ public static void execute( final java.lang.Runnable runnable )
{ runnable.run()/* späte Bindung, Polymorphie! */; }}
public final class Main
{ public static void main( final java.lang.String[] args )
{ Computer.execute( new ProgramA() );
Computer.execute( new ProgramB() ); }}
java.lang.System.out
A
B
The refactor “replace if by polymorphism”
“This is the heart of why ObjectOrientedProgramming is better. Replacing a flat file with a real database should be as easy as redirecting a stream from the console to a printer. -- Phlip ” – http://c2.com/cgi/wiki?ReplaceConditionalWithPolymorphism
Main.java
final class Rectangle
{ final double width;
final double height;
Rectangle( final double width, final double height )
{ this.width = width;
this.height = height; }}final class Circle
{ final double radius;
Circle( final double radius )
{ this.radius = radius; }}final class Util
{ static double circumference( final java.lang.Object object )
{ double result = -1;
if( object instanceof Rectangle )
{ final Rectangle rectangle =( Rectangle )object;
result = 2 * rectangle.width + 2 * rectangle.height; }
else if( object instanceof Circle )
{ final Circle circle =( Circle )object;
result = 2 * java.lang.Math.PI * circle.radius; }
return result; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ java.util.Arrays.asList
( Util.circumference( new Rectangle( 2, 2 )),
Util.circumference( new Circle( 2 ))).
forEach( java.lang.System.out::println ); }}transcript
8.0
12.566370614359172Main.java
final class Rectangle
{ final double width;
final double height;
Rectangle( final double width, final double height )
{ this.width = width;
this.height = height; }
double circumference()
{ return 2 * this.width + 2 * this.height; }}final class Circle
{ final double radius;
Circle( final double radius )
{ this.radius = radius; }
double circumference()
{ return 2 * java.lang.Math.PI * this.radius; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ java.util.Arrays.asList
( new Rectangle( 2, 2 ).circumference(),
new Circle( 2 ).circumference() ).
forEach( java.lang.System.out::println ); }}transcript
8.0
12.566370614359172
Hinzufügen eines weiteren Typs
Main.java
final class Rectangle
{ final double width;
final double height;
Rectangle( final double width, final double height )
{ this.width = width;
this.height = height; }}final class Circle
{ final double radius;
Circle( final double radius )
{ this.radius = radius; }}final class Triangle
{ final double a;
final double b;
final double c;
Triangle( final double a, final double b, final double c )
{ this.a = a;
this.b = b;
this.c = c; }}final class Util
{ static double circumference( final java.lang.Object object )
{ double result = -1;
if( object instanceof Rectangle )
{ final Rectangle rectangle =( Rectangle )object;
result = 2 * rectangle.width + 2 * rectangle.height; }
else if( object instanceof Circle )
{ final Circle circle =( Circle )object;
result = 2 * java.lang.Math.PI * circle.radius; }
else if( object instanceof Triangle )
{ final Triangle triangle =( Triangle )object;
result = triangle.a + triangle.b + triangle.c; }
return result; }}public class Main
{ public static void main( final java.lang.String[] args )
{ java.util.Arrays.asList
( Util.circumference( new Rectangle( 2, 2 )),
Util.circumference( new Circle( 2 )),
Util.circumference( new Triangle( 1, 2, 3 ))).
forEach( java.lang.System.out::println ); }}transcript
8.0
12.566370614359172
6.0Main.java
final class Rectangle
{ final double width;
final double height;
Rectangle( final double width, final double height )
{ this.width = width;
this.height = height; }
double circumference()
{ return 2 * this.width + 2 * this.height; }}final class Circle
{ final double radius;
Circle( final double radius )
{ this.radius = radius; }
double circumference()
{ return 2 * java.lang.Math.PI * this.radius; }}final class Triangle
{ final double a;
final double b;
final double c;
Triangle( final double a, final double b, final double c )
{ this.a = a;
this.b = b;
this.c = c; }
double circumference()
{ return this.a + this.b + this.c; }}public class Main
{ public static void main( final java.lang.String[] args )
{ java.util.Arrays.asList
( new Rectangle( 2, 2 ).circumference(),
new Circle( 2 ).circumference(),
new Triangle( 1, 2, 3 ).circumference() ).
forEach( java.lang.System.out::println ); }}transcript
8.0
12.566370614359172
6.0
Wir haben das Open-Closed-Principle erfüllt!
- Aussprachehinweis
- closed clozd
Erklärung zum Vorteil der OOP
Dadurch das die Verbimplementation mit dem jeweiligen Typ gespeichert werden, kann ein OO-System erweitert werden, ohne daß vorhandene Einheiten verändert werden müssen.
Die ursprüngliche Definition
Der Begriff “polymorphism ” wurde für die Informatik 1967 von Christopher Strachey in “Fundamental concepts in programming languages. Lecture Notes from International Summer School in Computer Programming, Copenhagen, 1967 ” geprägt. Er unterscheidet zwischen “ad-hoc polymorphism ” und “parametric polymorphism ”. Seine Definition sei hier auszugsweise wiedergegeben:
- In ad hoc polymorphism there is no simple systematic way of determining the type of the result from the type of the arguments. There may be several rules of limited extent which reduce the number of cases, but these are themselves ad hoc both in scope and content. All the ordinary arithmetic operations and functions come into this category. It seems, moreover, that the automatic insertion of transfer functions by the compiling system is limited to this class.
- Parametric polymorphism is more regular and may be illustrated by an example. Suppose f is a function whose argument is of type α and whose result is of type β (so that the type of f might be written α → β), and that L is a list whose elements are all of type α (so that the type of L is αlist). We can imagine a function, say Map, which applies f in turn to each member of L and makes a list of the results. Thus Map[f,L] will produce a βlist. We would like Map to work on all types of list provided f was a suitable function, so that Map would have to be polymorphic. However its polymorphism is of a particularly simple parametric type which could be written (α → β, αlist ) → βlist, where α and β stand for any types.
- When Strachey first coined the word polymorphism, he distinguished between ad hoc polymorphic functions which have different meanings for different types and parametric polymorphic functions which have uniform behaviour for all types.
- The term polymorphism was introduced by Christopher Strachey to express the use of a single operation symbol with different meanings in a programming language. He distinguished two main forms of polymorphism, which he called ad hoc and parametric.
- / Umbenennen
- Benennen Sie in dem ersten Programm dieser Lektion die Klasse »Object« in »C« um, und die Blockvariable »object« in »x«!