Fehlendes Verständnis für dynamische Bindung und Vererbung

Diskutiere Fehlendes Verständnis für dynamische Bindung und Vererbung im Java Basics - Anfänger-Themen Bereich.
M

Mika34

Hallo an Alle,
Ich versuche mir mittels eines Buches die Grundprinzipien von Java anzueignen. Nun bin ich an der Stelle der Vererbung und der dynamischen Bindung und komme gerade nicht weiter :( . Der Beispielcode sieht wie folgt aus:
Java:
class Parent {
public String getFoo() { return "parentFoo"; }
}
class Child extends Parent {
public String getFoo() { return "childFoo"; }
public String getBar() { return "childBar"; }
}
class GrandChild extends Child {
public String getFoo() { return "grandChildFoo"; }
}
Parent p0 = new Child();
p0.getFoo();
p0.getBar();
Child c0 = new GrandChild();
c0.getFoo();
c0.getBar();
GrandChild gc0 = new GrandChild();
gc0.getFoo();
gc0.getBar();
Hier verstehe ich die gesamten Ausdrücke nicht.

Parent p0 = new Child();


Child c0 = new GrandChild();


GrandChild gc0 = new GrandChild();

So wie ich es bis jetzt verstanden habe, erstellt die Klasse Parent ein Objekt der Kindklasse Child. Hierbei verstehe ich dann aber nicht inwiefern die Methoden der Klasse Parent und Child miteinander funktionieren sollen. Denn das ist mein zweites Problem.
Wenn man

p0.getFoo();

ausgibt, dann wird die Methode getFoo() aus der Child Klasse aufgerufen. Wenn man jedoch

p0.getBar();

ausführen will, dann kommt es zu einem Kompilierfehler. Weshalb kommt er hier zustande, die Methode befindet sich doch in der Child Klasse und demnach ist es doch auch ausführbar oder nicht?

Ich bin für jede Hilfe sehr dankbar

Liebe Grüße
 
H

httpdigest

So wie ich es bis jetzt verstanden habe, erstellt die Klasse Parent ein Objekt der Kindklasse Child.
Das ist ein Satz, der nicht viel Sinn ergibt. Eine Klasse erzeugt kein Objekt einer anderen Klasse.
Du hast eine Oberklasse/Superklasse "Parent" und zwei andere Klassen "Child" und "GrandChild" erben bzw. leiten von dieser "Parent" Klasse ab.
Das heißt in diesem Fall, dass die Kindklassen "Child" und "GrandChild" die Methode getFoo() der Oberklasse "Parent" überschreiben. Für solche Methoden wird immer jene überschreibende Version der Methode in der am meisten abgeleiteten Klasse aufgerufen, die diese Methode definiert.
Du benötigst hier auch noch die Begriffe "Compilezeittyp" und "Laufzeittyp". Ersterer wird auch "statischer Typ" und letzterer "dynamischer Typ" genannt.
Der Compilezeittyp ist der Typ eines Ausdrucks und der Laufzeittyp ist der Typ eines Objektes.
Im Falle:
Java:
Parent p0 = new Child();
ist der statische Typ der Variablen `p0` gleich "Parent" und der Typ des Objektes, welches in dieser Variabe gespeichert ist, ist "Child".
Methoden können nur aufgerufen werden, wenn diese von dem statischen Typ auch deklariert werden. Der statische Typ "Parent" deklariert die Methode "getBar()" nicht, also kann sie auch nicht aufgerufen werden. Hier ist der dynamische Typ irrelevant.
Der dynamische Typ kommt dann zum Tragen, um herauszufinden, welche konkrete Überschreibung der aufgerufenen Methode nun ausgeführt werden soll. Und die am meisten abgeleitete Version der Methode "getFoo()" (gemessen ab der Klasse "Child") ist nunmal in der Klasse "Child", also wird diese Methode aufgerufen.

p0 ist eine Instanz der Klasse Parent
Nicht ganz. Der statische Typ bzw. Compilezeittyp der Variablen `p0` ist Parent. `p0` selbst ist keine Instanz, sondern eine Variable, die zum Zeitpunkt der Ausführung eine Instanz des Typs `Child` speichert.
 
J

JustNobody

Ich versuche es mal etwas bildlich darzustellen:

Also schauen wir erst einmal an, was die Vererbung ist. Wenn du hast:
Java:
class Child extends Parent
Dann wird da eine Klasse Child erzeugt und ein Child ist ein Parent. -> Das ist sozusagen eine "ist ein" Beziehung.

Das ist das, was uns auch im echten Leben vorkommt:
Katze ist ein Säugetier ist ein Tier ist ein Lebewesen.

Nun kannst Du Variablen haben. Diese müssen einen Typ haben. Wenn wir bei den Tieren bleiben, dann sind Variablen etwas wie Käfige.

Du kannst also einen Käfig haben für ein Lebewesen. Da kann dann also eine Katze ebenso rein wie ein Hund oder eine Laus.

Und die Klassen haben ein "Verhalten" - das sind dann die Methoden, die da so drauf sind:
Eine Katze hat von mir aus streicheln(), schnurren(), ....
Ein Säugetier kann Tiere säugen: säugen(Säugetier).
Ein Lebewesen hat von mir aus einen Stoffwechsel.... so kann man sich vieles vorstellen.

Wenn Du nun diese Käfige (Variablen) hast, dann siehst Du nicht, was drin ist. Also der Käfig für ein Lebewesen: Da kannst Du nicht sagen: Das Lebewesen streichel ich jetzt. Es mag zwar sein, dass da eine Katze in dem Käfig ist, aber das weisst Du nicht. Und nur weil Du in den Käfig eine Katze gesteckt hast: Evtl. hab ich das Tier ja ausgetauscht....

Daher: Wenn Du einen Käfig für irgendein Lebewesen hast, dann solltest Du da immer erst einmal rein schauen und Prüfen:
Ist da eine Katze drin? -> Katze streicheln

Und wenn Du also eine Katze streicheln willst, dann nimm auch gleich einen Käfig für eine Katze. Dann kannst Du sicher sein, dass da kein anderes Tier rein kommt. Und dann kannst Du direkt das Tier aus dem Käfig streicheln....
 
M

Mika34

Vielen Dank für die vielen Antworten. Jetzt ist es mir um einiges klarer!
Wenn ich es jetzt richtig verstanden habe dann läuft es doch eigentlich so:
Wenn ich eine Instanz vom Typ Parent mit Child initalisiere, dann werden alle Attribute und Methoden von Parent geerbt, oder nicht?
EDIT:Wenn die Childklasse von der Superklasse Parent erbt natürlich.
Und diese Methoden können, aber müssen nicht, dann überschrieben werden von der Kindklasse.
 
H

httpdigest

Wenn ich eine Instanz vom Typ Parent mit Child initalisiere
Hier muss man ganz genau aufpassen. Man kann keine Instanz vom Typ Parent mit Child initialisieren.
Der Begriff "Instanz" bedeutet quasi: Objekt, also Instanz einer Klasse.
Eine Variable ist keine Instanz.
Du kannst eine Variable (oder generell auch einen Ausdruck) vom Typ (bzw. mit dem statischen Typ) Parent mit einer Instanz vom Typ Child initialisieren bzw. zuweisen. Das geht.
 
J

JustNobody

Vielen Dank für die vielen Antworten. Jetzt ist es mir um einiges klarer!
Wenn ich es jetzt richtig verstanden habe dann läuft es doch eigentlich so:
Wenn ich eine Instanz vom Typ Parent mit Child initalisiere, dann werden alle Attribute und Methoden von Parent geerbt, oder nicht?
Und diese Methoden können, aber müssen nicht, dann überschrieben werden von der Kindklasse.
Ich vermute, dass Du das Richtige meinst. Wenn eine Klasse Child von Parent abgeleitet wird, dann erbt die alle Attribute und Methoden vom Parent. ==> Das ist also etwas, das auf Klassenebene statt findet. Und auf Klassenebene kann dann die Subklasse Methoden überschreiben.

Wenn du aber nun etwas in eine Variable vom Typ Parent packst, dann muss dieses etwas ein "Parent sein", also entweder ein Parent oder von Parent abgleitet. Damit stehen die Möglichkeiten vom Parent zur Verfügung.
 
M

Mika34

Alles klar. Aber eines ist mir immer noch nicht ganz klar, wenn ich ohne jegliche Vererbung eine Referenz vom Typ Parent erstelle und diese mit Child initialisiere, dann wird an sich doch gar nichts vererbt also an Attributen und Methoden oder doch?
EDIT: Ist es überhaupt möglich ohne Vererbung, also ohne "extends Parent" oder schlägt es dann fehl?
 
T

temi

wenn ich ohne jegliche Vererbung eine Referenz vom Typ Parent erstelle und diese mit Child initialisiere
Meinst du damit, dass du zwei unabhängige Klassen Parent und Child hast?

Dann kannst du einer Variablen vom Typ Parent keine Instanz vom Typ Child zuweisen. Das wäre, wie einen String an eine Integer-Variable zuweisen zu wollen.

In eine Variable vom Typ Parent kann nur eine Instanz von Parent oder einem von Parent abgeleiteten (extents) Typen zugewiesen werden.
 
H

httpdigest

Was meinst du mit "ohne jegliche Vererbung"? Wenn du eine Klasse "Parent" hast und eine davon unabhängige Klasse "Child" und Child erbt nicht von Parent (hat also kein "extends Parent"), dann kannst du auch kein Objekt vom Typ Child einer Variablen vom Typ Parent zuweisen. Die Zuweisung funktioniert dann nicht.
 
T

temi

Was man übrigens noch wissen sollte:

In Java erben alle Klassen automatisch von der Klasse Object. Einer Variable vom Typ Object kann man demnach alles (außer primitive Typen, bzw. diese nur mittels Wrapper-Klassen) zuweisen.
 
W

White_Fox

Wie wäre es eigentlich, mal ein "
Ja, genau das war meine Frage.
Denk immer an das, was dir JustNobody mit dem Katzenbeispiel versucht hat klarzumachen:
Wenn du eine Superklasse 'Lebewesen' hast, von dieser eine Klasse 'Säugetier' ableitest und von 'Säugetier' die Klasse 'Katze' ableitest. Dann kannst du schreiben:
Java:
Säugetier tier = new Katze();  //Ein Katzeobjekt erstellen und einer Variable Säugetier zuweisen.
Denn: eine Katze ist ja bekanntlich auch ein Säugetier.
Wenn du aber von deiner Klasse 'Lebewesen' auch noch die Klasse 'Fisch' ableiten würdest, könntest du einer Variable vom Typ Fisch NICHT ein Katzeobjekt zuweisen, denn eine Katze ist nunmal kein Fisch.

Was allerdings wiederum geht: Einer Variablen vom Typ 'Lebewesen' ein Objekt vom Typ Goldfisch (extends Fisch) oder ein Katzeobjekt zuweisen, denn sowohl der Fisch als auch die Katze sind vom Typ 'Lebewesen'.

Der ganze Vererbungskram mit Klassen und Interfaces ist in Java geradezu lächerlich einfach, wenn man es mal begriffen hat. (Nichtsdestotrotz hat unser Prof aber ein ganzes Semester nur darauf verwendet, uns den Unterschied zwischen einer Klasse und einem Objekt einzubläuen, es ist also durchaus nicht ungewöhnlich, es nicht gleich zu verstehen.)
Dennoch finde ich das Konzept einfach genial, gerade weil sich trotz der Einfachheit ganz erstaunliche Dinge damit machen lassen.
 
Thema: 

Fehlendes Verständnis für dynamische Bindung und Vererbung

Passende Stellenanzeigen aus deiner Region:
Anzeige

Anzeige

Anzeige
Oben