![]() |
|
| #1 (permalink) | |
|
Stammbenutzer
Megabyte
Registriert seit: 18.01.2008
Beiträge: 2.467
Abgegebene Danke: 23
Erhielt 33 Danke für 33 Beiträge
|
Dies ist ein kleiner Crash-Kurs in Sachen Vererbung und Polymorphie im Allgemeinen. Polymorphie ist ein
wesentlicher, wenn nicht der wesentlichste Bestandteil von OO-Programmierung. Los geht's: Vererbung Vererbung bedeutet: Wenn man mehrere Objekte hat, die in Teilen identisch sind, d.h. in ihrem Aussehen oder ihren Eigenschaften (Attribute) oder ihrem Verhalten (Methoden) teils gleich sind, sollte man Vererbung verwenden. Dazu findet man eine sog. "Oberklasse / Superklasse" für all diese Objekte. Sie besteht dann genau aus diesen Dingen, in denen sich alle Objekte gleich sind. Wenn man nun mehrere solch ähnliche Objekte hat, kann man einfach von der Oberklasse erben, und spart sich dadurch erstens viel Schreibarbeit, und nutzt zum zweiten (und das ist das wichtige) die Polymorphie von Java. Bsp: Code:
public class Auto{
private int ps;
private String marke;
private String modell;
private Color lackFarbe;
private double marktWert;
private String baujahr;
public Auto( String marke, String modell, String baujahr){
this.marke = marke;
this.modell = modell;
this.baujahr = baujahr;
}
// ...
}
der Klasse Auto. Statt also zweimal die gleiche Klassenstruktur aufzuziehen, mache ich Auto zur Superklasse und erbe dann davon: Code:
public class BMW extends Auto{}
public class Audi extends Auto
Java nimmt überall dort, wo eine Klasse erwartet wird, auch jede Unterklasse dieser Klasse an. So könnte ich zB die Methode Code:
kaufeAuto (Auto a){...}
Code:
kaufeAuto (new BMW( "BMW", "7er", "2007")); Code:
kaufeAuto (new Audi( "Audi", "A8", "2005")); sodass an jeder Stelle im Programm jeweils nur die Informationen gekapselt sind, die mich interessieren. Damit meine ich: Wenn ich eine Methode habe Code:
verkaufeAuto (Auto a){...}
nun ein BMW oder ein Audi oder sonst was ist. Ich bin ja kein BMW-Händler oder Audi-Händler. Ich bin Auto-Händler, und was ich will, das sind Autos. Und diese Abstrahierung von einem BMW zu einem "Auto" ganz generell ist der Polymorphismus: Die Klasse Autohändler kennt keine spezifischen Autos, sondern nur allgemein Autos. D.h. auch, dass ich jederzeit neue Auto-Unterklassen erstellen oder löschen kann, ohne dass ich im Code von "Autohändler" etwas ändern muss. Vererbung, Sichtbarkeit und Overriding Im Zusammenhang mit Vererbung sollte man auch das Kapitel der Sichtbarkeit nicht weglassen. Es gibt in Java 3 verschiedene Schlüsselwörter, mit denen man die Sichtbarkeit einer Variablen oder einer Methode spezifizieren kann. "Variablen oder Methode" bezeichne ich im Folgenden als v/m : 1) private 2) protected 3) public private heisst: v/m ist nur in der Klasse, in der die v/m definiert ist, sichtbar. protected heisst: v/m ist in allen Klassen sichtbar, die sich im selben Package befinden wie die Klasse, in der die v/m definiert ist. public heisst: v/m ist überall sichtbar Anmerkung: Man kann ein Schlüsselwort auch komplett weglassen, ich bin mir nicht sicher aber ich glaube dann ist es immer automatisch protected, oder irgendein Zwischending zwischen protected und was anderem Ist nicht so wichtig, weil man sowas eh nie machen sollte.Damit klar ist, was ich mit "sichtbar" meine: Sichtbar heisst einfach, ich kann auf die v/m zugreifen an einer gewissen Stelle/Klasse im Programm. Wie hängt das jetzt also mit Vererbung zusammen? public und protected v/m werden vererbt und sind in der Unterklasse sichtbar. private v/m werden implizit auch vererbt, sind aber in Unterklassen nicht sichtbar. Hier besteht also der kleine aber feine Unterschied bei private v/m: Sie sind weder in Unterklassen noch in komplett anderen Klassen sichtbar, aber wenn man sie vererbt, sind sie in der Unterklasse "da", wenn auch versteckt. Ich denke ohne Beispiel wird das nicht ganz klar: Code:
public class Auto{
private int ps;
public Auto (int ps){
this.ps = ps;
}
public int getPs(){
return this.ps;
}
}
Code:
public class BMW extends Auto{
public void pimp(){
this.ps += 20; // Compile-Error: Variable "ps" nicht bekannt
}
}
Code:
BMW myBmw = new BMW(200); myBmw.getPs(); // <- liefert "200", also gibt es "ps" scheinbar doch ?! eine Unterklasse ist, und sie demnach alle Attribute und Methoden der Superklasse hat. Zugreifen tut man auf private Attribute immer mit - öffentlichen (public) - "Gettern" und "Settern". Jetzt noch zu dem Wort "Override": Das heisst ßberschreiben von Methoden aus der Superklasse in Unterklassen. Wenn wir uns das obige Bsp mit Auto und BMW ankucken, wäre das hier ein Override: Code:
public class BMW extends Auto{
@Override
public int getPs(){
// ein Aufruf von bmw.getPs() liefert jetzt nicht mehr "ps" der Oberklasse, sondern das,
// was hier drin steht!! -> Methode wurde überschrieben!
return 10;
}
}
Dennoch ist die ursprüngliche Methode getPs() der Superklasse "Auto" nicht verloren. Man greift auf Attribute und Methoden der Oberklasse per "super" zu: Code:
@Override
public int getPs(){
return super.getPs(); // ßberschrieben aber wieder zur Oberklassen-Methode gelenkt.
// Macht eindeutig eher nicht so viel Sinn...
}
Mehrfach-Vererbung: Es sei noch erwähnt, dass "echte" Mehrfach-Vererbung in Java nicht möglich ist: Code:
public class BMW extends Auto extends Fahrzeug{} // Compile-Fehler!
erreichen. so geht folgendes ohne Probleme: Code:
public class Fahrzeug{}
public class Auto extends Fahrzeug{}
public class BMW extends Auto{}
mein Autohändler also noch allgemeiner wäre, und nicht nur Autos annehmen würde sondern auch Motorräder, könnte ich in einer Methode Code:
verkaufe ( Fahrzeug f){}
das Schlüsselwort 'abstract' Im Zusammenhang mit Vererbung wird auch oft das Keyword 'abstract' benutzt. Die Bedeutung ist im Prinzip einfach der deutschen ßbersetzung zu entnehmen: "Abstrakt" ist ein Ding, dass es so in der Realität gar nicht gibt, sondern nur eine Art Muster oder Rohfassung etc. ist... Abstrakte Klassen: ... heisst jetzt also, ich habe etwas, das es so an sich gar nicht gibt, Bsp: Code:
public abstract class Auto{}
und sagen "ich möchte ein Auto". Das gibt es gar nicht. Es gibt nur BMW, Audi, VW, ... kein "Auto". Deshalb ist die Klasse Auto ein gutes Beispiel für eine abstrakte Klasse. Was bringt das nun? Naja, wenn eine Klasse abstrakt ist, heisst das einfach nur: Man kann keine Instanz davon erstellen: Code:
new Auto(); // Compile-Error: Auto ist abstrakt, kann kein "Auto" erstellen versuchen kann, ein Auto zu erstellen. Mehr nicht, man kann es genauso gut weglassen wenn man selbst darauf achtet, nur spezifische Unterklassen zu erstellen. Abstrakte Methoden: Abstrakte Methoden sind jetzt aber schon vielmehr auch praktisch nützlich und ein ganz wesentlicher Bestandteil von Polymorphie! ßber Vererbung habe ich gesagt, man nimmt sie, wenn Teile verschiedener Objekte gleich sind. Vererbung zusammen mit abstrakten Methoden nimmt man dahingegen dann, wenn die Funktionalität von den Objekten gleich sein soll (was sie können), aber die Implementierung unterschiedlich ist auf Grund der Unterschiede zwischen den Klassen. (und nicht gleich wie bei Vererbung). Bsp: Ein "Kreis" und ein "Quadrat" sind beides geometrische Figuren. Sie haben beide zB eine Position im Koordinaten System (x,y), haben beide eine gewisse Grösse (Durchmesser bzw. Kantenlänge), und können zB auch beide in einem JPanel gemalt werden. Man kann das aber nicht mit "reiner" Vererbung lösen: Code:
public abstract class GeometrischeFigur{
public void male(Graphics g){
// tja, wie malt sich eine "geometrische Figur". Was sind die Gemeinsamkeiten im Zeichenvorgang
// von Kreisen und Quadraten?? Antwort: KEINE!
}
}
public class Kreis extends GeometrischeFigur
public class Quadrat extends GeometrischeFigur
Figuren funktioniert. Die einzige Lösung wäre über irgendwelche Abfragen: Code:
public void male(Graphics g){
if ( this instanceof Kreis ){
g.drawOval(...)
}
else if (this instanceof Quadrat){
g.drawRect(...)
}
}
Viel Spass beim if-else-Hirntod! Und noch schlimmer: Wenn es nicht nur die Methode male() für jedes geometrische Objekt geben soll, sondern auch noch die Methoden "verschiebe()", "drehe()", "färbe()", ... Wie lange hockt man dann davor, um eine einzige neue Funtkionalität zu implementieren? Die Lösung für das Problem sind abstrakte Methoden. Eine abstrakte Methode wird in der Superklasse nur definiert, nicht deklariert,also nicht implementiert: Code:
public abstract class GeometrischeFigur{
public abstract void male(Graphics g); // kein Rumpf!
}
es halt passt. Die Klassen "Kreis" und "Quadrat" würden jetzt so aussehen: Code:
public class Kreis extends GeometrischeFigur{
@Override
public void male(Graphics g){
g.drawOval(...); // eigene Implementierung: zeichen Kreis
}
}
Code:
public class Quadrat extends GeometrischeFigur{
@Override
public void male(Graphics g){
g.drawRect(...); // eigene Implementierung: zeichen Rechteck
}
}
Methoden, d.h. ein "Geometrie-Scanner" würde alle Typen von Superklasse GemoetrischeFigur erkennen, und das gute ist, er kannauch alle korrekt malen: Code:
/* Dummer Klassen-Name: Verwaltet einfach geometrische Figuren und macht etwas damit */
public class Scanner{
private List<GeometrischeFigur> meineFiguren;
public void maleAlles(Graphics g){
for( GeometrischeFigur gf : meineFiguren ){
gf.male(g);
}
}
}
was. Ihm ist es aber egal: Er weiss, er hat geometrische Objekte, und diese Klasse bietet die Methode "male()", also kann er sie aufrufen. Was genau da passiert ist ihm Wurscht. Und das übernehmen halt die einzelnen Unterklassen davon jeweils für sich. Interfaces Es gibt kaum einen Unterschied zwischen Interfaces und abstrakten Methoden: Code:
public abstract class Paintable{
public abstract void paint();
//Bem.: Sobald eine Klasse eine abstrakte Methode hat,
//MUSS die Klasse selbst auch abstrakt sein.
}
Code:
public interface Paintable{
public void paint();
}
Man könnte also die zweite Klasse, von der man erben will, als Interface erstellen und dieses dann implementieren. Beachte, dass man bei Interfaces so viele implementieren kann, wie man will: Code:
public class MegaClass extends MegaPower implements Interface1, Interface2, Interface3, .... // <-Okay! nur hat das Erben immer eine Einschränkung auf Sicht der Polymorphie. Deshalb gilt i.d.R: Code:
Wenn man die Wahl hat zwischen der Implementierung eines Interfaces oder der Erbung von einer Klasse mit abstrakten Methoden, sollte man immer lieber zum Interface greifen! Zusammenfassung / praktische Tipps Manch einer denkt sich jetzt vllt: Das ist ja alles schön und gut, aber nur weil man jemandem die Regeln von Schach erklärt hat, heisst das noch lange nicht, dass er ein Spiel auch nur annähernd gewinnen kann. Ich hab diese Verbindung zu Schach mal irgendwo gelesen und fand sie extrem passend! Programmieren ist wie Schach: Man lernt es, indem man es tut! Trotzdem gibt es wie auch im Schach ein paar Tipps, ein paar Regeln, die einem einen Stubs in die richtige Richtung geben können. Wenn man weiss, auf was man achten muss, erkennt man auch Gefahren. Die folgenden Dinge kann man sich durchaus auf ein Blatt Papier schreiben und immer mal wieder durchlesen, bis man sie verinnerlicht hat. Sie helfen beim Schreiben von gutem Design/Code: 1) Switch-Konstrukte oder nicht-elementare if-Statements (alles, was nicht mit <,>=,==,|| etc geprüft wird) weisen auf einen Design-Fehler hin, den man durch Polymorphismus lösen kann. Bsp: Methode male() vor und nach Benutzung von abstrakten Methoden 2) Vererbte Dinge sind immer in den Unterklassen da, aber nur public v/m sind echt "sichtbar" 3) Man verwendet immer private für Attribute einer Klasse und macht sich Getter/Setter um in Unterklassen (und evtl auch aus anderen Klassen) darauf zugreifen zu können 4) Vererben tut man, wenn verschiedene Objekte in Teilen gleich sind und sich gleich Verhalten 5) Abstrakte Klassen nutzt man, wenn es die Oberklasse an sich gar nicht gibt 6) Abstrakte Methoden nutzt man dann, wenn man vererben möchte, die Funktionalitäten aber in jeder Unterklasse anders implementiert werden müssen, und nicht gleich sind. 7) die Implementierung eines Interfaces ist sowas wie das Implementieren von abstrakten Methoden einer Superklasse, nur besser! Hoffe, ich konnte ein paar Leuten etwas klarer machen. Wer Fehler findet oder Anregungen hat, was ich noch vergessen hab, bitte Bescheid geben! |
|
|
|
| Danke sagt: |
d3rbastl3r (20.07.2010)
|
|
| Lesezeichen |
Latex Maths & Physics Editor ...
|
| Themen-Optionen | Thema durchsuchen |
| Ansicht | |
|
|
Ähnliche Themen
|
||||
| Thema | Autor | Forum | Antworten | Letzter Beitrag |
| Polymorphie Knobelspaß ;-) | dangermouse78 | Allgemeine Java-Themen | 15 | 24.10.2008 01:32 |
| Vererbung oder Polymorphie? | zuro | Java Basics - Anfänger-Themen | 5 | 29.04.2007 14:26 |
| Polymorphie | JavaBeginner2 | Java Basics - Anfänger-Themen | 6 | 25.01.2006 22:43 |
| Polymorphie und Vererbung | sumatra | Java Basics - Anfänger-Themen | 25 | 18.06.2004 23:35 |
| Problem mit Polymorphie... | ramonl | Allgemeine Java-Themen | 7 | 16.01.2004 15:47 |