Statische und Dynamische Bindung

Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.

Statische und Dynamische Bindung
Hallo, ich hätte da mal eine Frage bezüglich der statischen und dynamischen Bindung in Java. Ich habe mir hierfür mal ein Testprogramm geschrieben:

class Test {
	String a = "Ober!";
	
	void printString() {
		System.out.println(a);
	}
}

class Child extends Test {
	String a = "Unter!";
	
	void printString() {
		System.out.println(a);
	}
}

class Haupt {
	public static void main(String [] args) {
	Test t1 = new Test();
	Test t2 = new Child();
	t1.printString();
	t2.printString();
	System.out.println(t1.a);
	System.out.println(t2.a);
	t2.a ="Neu!";
	t1.printString();
	t2.printString();
	System.out.println(t1.a);
	System.out.println(t2.a);
	
	}
}

Als Ausgabe liefert das Ganze (ich nummeriere es mal):

  1. Ober!
  2. Unter!
  3. Ober!
  4. Ober!
  5. Ober!
  6. Unter!
  7. Ober!
  8. Neu!

Das ganze verwirrt mich ein wenig, meine bisherigen Gedanken dazu sind, dass Fall 1. und 2. Aufrufe von Methoden sind, also dynamische Bindung vorliegt. Hier wird also zur Laufzeit der Typ der Instanz betrachtet und die entsprechende Funktion aufgerufen, also “Ober!” und “Unter!”. In Fall 3. und 4. wird direkt auf Instanzvariablen zugegriffen, also wird statisch gebunden. t1 und t2 wurden als “Test” deklariert, demnach setzt der Compiler hier für beide Werte “Ober!” ein.
Was passiert jetzt bei “t2.a =“Neu!”;”? Der Variable a von t2 wird der Wert “Neu!” zugewiesen. In Fall 5. und 6. wird wieder dynamisch gebunden, Fall 5. spuckt wie zu erwarten “Ober!” aus, Fall 6. jedoch “Unter!”. Wenn hier die Funktion printString() der Klasse Child aufgerufen wird, würde ich hier ein “Neu!” erwarten, also dass in printString() die Variable a von t2 ausgelesen und ausgegeben wird. Wie kommt hier jetzt das “Unter!” zustande?
Weiterhin in Fall 7. und 8. sollte statisch gebunden werden. Für Fall 8. könnte ich mir hier vorstellen dass der Compiler durch die statische Bindung gleich den Wert “Neu” einsetzt, da dies der letzte zugewiesene Wert auf die Variable t2.a ist.
Ich hoffe jetzt natürlich dass jemand mit mehr Durchblick meine Unklarheiten beseitigen kann, im Moment kommt mir dieses Verhalten sehr unintuitiv vor und es wäre schön wenn jemand meine Annahmen richtigstellen könnte.


Ohne zu garantieren, dass das, was ich jetzt schreibe stimmt, würde ich folgendes behaupten. Bei t2.a = "Neu!" wird der Variable String a der Klasse Test der Wert „Neu!“ zugewiesen und nicht der Variable a der Klasse Child wegen der statischen Bindung. Deswegen wird bei 6. bei dem Aufruf t2.printString(); „Unter!“ ausgegeben, da hier die Methode printString() der Klasse Child wegen der dynamischen Bindung aufgerufen wird, die auf die Instanzvariable a der Klasse Unter zugreift, die du vorhin NICHT verändert hast.


Soweit richtig, auch wenn ich mir nicht sicher bin, ob der Compiler das Lesen aus den Instanzvariablen von [m]t1[/m] und [m]t2[/m] wirklich wegoptimiert. Das könntest du mittels [m]javap -c[/m] selbst mal nachschauen. (Wild guess: Es wird nicht optimiert, weil der Compiler nicht garantieren kann, dass die Variable zwischenzeitlich nicht durch einen Seiteneffekt einer anderen Methode/Klasse/etc. verändert wurde. Um das genau sagen zu können müsste ich aber die Regeln zur Sichtbarkeitssynchronisation in Java kennen.)

[m]t2.a = „Neu“;[/m] bindet statisch, d.h. die Eigenschaft [m]a[/m] der Klasse [m]Test[/m], also des statischen Typs von [m]t2[/m]. Diese wird hier überschreiben. In [m]t2.printString()[/m] ist der statische Typ von [m]this[/m] (das immplizit beim Zugriff auf [m]a[/m] ja benutzt wird) aber [m]Child[/m], d.h. die verwendete Variable ist [m]a[/m] aus [m]Child[/m] – diese kannst du aber in deiner [m]main[/m] gar nicht überschreiben, weil der Compiler ja keine Variable mit dem statischen Typ [m]Child[/m] kennt. Würdest du [m]((Child) t2).a = „Neu!“;[/m] schreiben würdest du das von dir erwartete Verhalten sehen.

[quote=Meathook]Weiterhin in Fall 7. und 8. sollte statisch gebunden werden. Für Fall 8. könnte ich mir hier vorstellen dass der Compiler durch die statische Bindung gleich den Wert „Neu“ einsetzt, da dies der letzte zugewiesene Wert auf die Variable t2.a ist.[/quote] Wiederum weiß ich nicht, ob der Compiler hier wirklich optimiert, aber der statische Typ der Variable ist ja [m]Test[/m], folglich wird die Variable [m]a[/m] aus [m]Test[/m] verwendet.
Eventuell wird das Problem klarer, wenn du die Variable in [m]Child[/m] einfach mal in [m]b[/m] umbenennst.


Super, ihr zwei habt mir wirklich geholfen, ich hatte einfach nicht daran gedacht, dass Child das a von Test noch als super.a hat. Jetzt ist auch klar wie die Ausgabe zustande kommt, vielen Dank!

Ich habe mein Beispiel nochmal etwas erweitert, damit man genauer sieht was hier passiert, falls noch jemand anderes Probleme mit der Thematik hat:

class Test {
	String a = "Ober!";
	
	void printString() {
		System.out.println("In Test.printString()");
		System.out.println(a);
		System.out.println("-----------------");
	}
}

class Child extends Test {
	String a = "Unter!";
	
	void printString() {
		System.out.println("In Child.printString()");
		System.out.print("a:");
		System.out.println(a);
		System.out.print("super.a:");
		System.out.println(super.a);
		System.out.println("-----------------");
	}
}

class Haupt {
	public static void main(String [] args) {
	Test t1 = new Test();
	Test t2 = new Child();
	
	t1.printString();
	t2.printString();
	System.out.println("System.out.println(t1.a);");
	System.out.println(t1.a);
	System.out.println("-----------------");
	System.out.println("System.out.println(t2.a);");
	System.out.println(t2.a);
	System.out.println("-----------------");
	
	System.out.println("t2.a = Neu!");
	t2.a ="Neu!";
	
	t1.printString();
	t2.printString();
	System.out.println("System.out.println(t1.a);");
	System.out.println(t1.a);
	System.out.println("-----------------");
	System.out.println("System.out.println(t2.a);");
	System.out.println(t2.a);
	System.out.println("-----------------");
	
	}
}

Ausgabe:

In Test.printString()
Ober!
-----------------
In Child.printString()
a:Unter!
super.a:Ober!
-----------------
System.out.println(t1.a);
Ober!
-----------------
System.out.println(t2.a);
Ober!
-----------------
t2.a = Neu!
In Test.printString()
Ober!
-----------------
In Child.printString()
a:Unter!
super.a:Neu!
-----------------
System.out.println(t1.a);
Ober!
-----------------
System.out.println(t2.a);
Neu!
-----------------

Edit: Danke für den Hinweis meisterT :slight_smile:


[ot]
empfehle statt [#]... ...[/#]
[/ot]

1 Like