Ich habe eine Frage zur Vererbung in Java-ähnlichen OO-Programmiersprachen. Es kam in meiner Compiler-Klasse vor, als ich erklärte, wie Methoden und deren Aufruf kompiliert werden. Ich habe Java als Beispielquellsprache zum Kompilieren verwendet.
Betrachten Sie nun dieses Java-Programm.
class A {
public int x = 0;
void f () { System.out.println ( "A:f" ); } }
class B extends A {
public int x = 1;
void f () { System.out.println ( "B:f" ); } }
public class Main {
public static void main ( String [] args ) {
A a = new A ();
B b = new B ();
A ab = new B ();
a.f();
b.f();
ab.f();
System.out.println ( a.x );
System.out.println ( b.x );
System.out.println ( ab.x ); } }
Wenn Sie es ausführen, erhalten Sie das folgende Ergebnis.
A:f
B:f
B:f
0
1
0
Die interessanten Fälle sind solche, die mit dem Objekt ab
vom statischen Typ auftreten A
, das B
dynamisch ist. Wie ab.f()
druckt aus
B:f
Daraus folgt, dass Methodenaufrufe nicht vom Typ der Kompilierungszeit des Objekts beeinflusst werden, mit dem die Methode aufgerufen wird. Wird jedoch
System.out.println ( ab.x )
ausgedruckt 0
, sodass der Mitgliederzugriff von den Typen zur Kompilierungszeit beeinflusst wird.
Ein Student fragte nach diesem Unterschied: Sollte der Zugang von Mitgliedern und Methoden nicht miteinander übereinstimmen? Ich könnte keine bessere Antwort finden als "das ist die Semantik von Java".
Würdest du einen klaren konzeptionellen Grund kennen, warum Mitglieder und Methoden in diesem Sinne unterschiedlich sind? Etwas, das ich meinen Schülern geben könnte?
Bearbeiten : Nach weiteren Untersuchungen scheint dies eine Java-Eigenart zu sein: C ++ und C # verhalten sich unterschiedlich, siehe z. B. Saeed Amiris Kommentar unten.
Edit 2 : Ich habe gerade das entsprechende Scala-Programm ausprobiert:
class A {
val x = 0;
def f () : Unit = { System.out.println ( "A:f" ); } }
class B extends A {
override val x = 1;
override def f () : Unit = { System.out.println ( "B:f" ); } }
object Main {
def main ( args : Array [ String ] ) = {
var a : A = new A ();
var b : B = new B ();
var ab : A = new B ();
a.f();
b.f();
ab.f();
System.out.println ( "a.x = " + a.x );
System.out.println ( "b.x = " + b.x );
System.out.println ( "ab.x = " + ab.x ); } }
Und zu meiner Überraschung ergibt sich daraus
A:f
B:f
B:f
a.x = 0
b.x = 1
ab.x = 1
Beachten Sie, dass die overrise
Modifikatoren erforderlich sind. Dies überrascht mich, weil Scala in die JVM kompiliert und sich beim Kompilieren und Ausführen des Java-Programms oben mit dem Scala-Compiler / der Laufzeit wie das Java-Programm verhält.
f
undx
, ohne dies ausdrücklich zu erwähnen, IMO C # Version ist besser ( mehr in OO Sicht akzeptabel), anstatt dies zu tun, definierenx
,f
wie virtuelle, und Sie werden in durch zwingende sie Konsistenz Ergebnis erhaltenB
. Aber wenn Sie dies in Stackoverflow fragen, werden Sie mehr Aufmerksamkeit bekommen und ich bin sicher eine nette Antwort.