Ich studiere das Überschreiben von Memberfunktionen in Java und überlege, ob ich mit Membervariablen überschreiben möchte.
Also habe ich Klassen definiert
public class A{
public int intVal = 1;
public void identifyClass()
{
System.out.println("I am class A");
}
}
public class B extends A
{
public int intVal = 2;
public void identifyClass()
{
System.out.println("I am class B");
}
}
public class mainClass
{
public static void main(String [] args)
{
A a = new A();
B b = new B();
A aRef;
aRef = a;
System.out.println(aRef.intVal);
aRef.identifyClass();
aRef = b;
System.out.println(aRef.intVal);
aRef.identifyClass();
}
}
Die Ausgabe ist:
1
I am class A
1
I am class B
Ich kann nicht verstehen, warum, wenn aRef auf b gesetzt ist, intVal noch Klasse A ist?
Wenn Sie eine Variable mit demselben Namen in einer Unterklasse erstellen, wird dies als hiding bezeichnet. Die resultierende Unterklasse enthält nun tatsächlich beide Eigenschaften. Sie können von der Superklasse aus mit super.var
oder ((SuperClass)this).var
darauf zugreifen. Die Variablen müssen nicht einmal vom selben Typ sein. Dies sind nur zwei Variablen, die einen Namen teilen, ähnlich wie zwei überladene Methoden.
Variablen sind in Java nicht polymorph; Sie überschreiben sich nicht.
Variablen werden zur Laufzeit während der Kompilierung aufgelöst. Der aRef ist vom Typ A, daher wird aRef.Intvalue zu 1 kompiliert.
Es gibt keinen Polymorphismus für Felder in Java.
Die Entscheidung Variables
geschieht zur Kompilierzeit, daher wird immer auf Base Class-Variablen (nicht auf untergeordnete Variablen von Kindern) zugegriffen.
Also, wann immer Upcasting passiert, immer daran denken
1) Auf Variablen der Basisklasse wird zugegriffen.
2) Sub-Klassen-Methoden (überschriebene Methoden, wenn das Überschreiben mit anderen geerbten Methoden vom übergeordneten Objekt passiert) werden aufgerufen.
OverRiding Concept in Java Funktionen werden je nach Objekttyp überschrieben und auf Variablen wird auf Referenztyp zugegriffen.
Für z.B.
Parent parent=new Child();
parent.behaviour();
Hier ist parent
eine Referenz der Parent-Klasse, enthält jedoch ein Objekt der Child-Klasse, daher wird in diesem Fall die Child-Klassenfunktion aufgerufen.
Child child=new Child();
child.behaviour();
Hier enthält child
ein Objekt der Child-Klasse, daher wird die Child-Klassenfunktion aufgerufen.
Parent parent=new Parent();
parent.behaviour();
Hier enthält parent
das Objekt der übergeordneten Klasse, daher wird die Funktion der übergeordneten Klasse aufgerufen.
Wenn Sie versuchen, auf die Variable zuzugreifen, hängt sie vom Objekt des Referenztyps ab, nicht vom Objekttyp.
Für z.B.
Parent parent=new Child();
System.out.println(parent.state);
Der Referenztyp ist Parent, sodass auf die Parent-Klassenvariable zugegriffen wird, nicht auf die Child-Klassenvariable.
Child child=new Child();
System.out.println(child.state);
Der Referenztyp ist hier Child, daher wird nicht auf die Child-Klassenvariable, sondern auf die Parent-Klassenvariable zugegriffen.
Parent parent=new Parent();
System.out.println(parent.state);
Hier ist der Referenztyp Parent, also wird auf Parent-Klassenvariable zugegriffen.
Ab JLS Java SE 7 Edition §15.11.1:
Dieses Fehlen einer dynamischen Suche nach Feldzugriffen ermöglicht die effiziente Ausführung von Programmen mit einfachen Implementierungen. Die Möglichkeit des späten Bindens und Überschreibens ist nur verfügbar, wenn Instanzmethoden verwendet werden.
Die Antworten von Oliver Charlesworth und Marko Topolnik sind korrekt. Ich möchte etwas näher auf den warum-Teil der Frage eingehen:
In Java Klassenmitgliedern sind auf die zugegriffen wird entsprechend dem Typ der Referenz und nicht dem Typ des eigentlichen Objekts. Aus demselben Grund könnten Sie, wenn Sie eine someOtherMethodInB()
in der Klasse B
hätten, nicht auf aRef
zugreifen, nachdem aRef = b
ausgeführt wurde. Bezeichner (z. B. Klassen-, Variablennamen usw.) werden zur Kompilierzeit aufgelöst, und der Compiler verwendet hierfür den Referenztyp.
Wenn Sie in diesem Beispiel System.out.println(aRef.intVal);
ausführen, wird der in intVal
definierte Wert von A
ausgegeben, da dies der Typ der Referenz ist, die Sie für den Zugriff darauf verwenden. Der Compiler sieht, dass aRef
vom Typ A
ist, und dies ist die intVal
, auf die er zugreifen wird. Vergessen Sie nicht, dass Sie beide Felder in den Instanzen von B
haben. JLS hat auch ein ähnliches Beispiel wie "15.11.1-1. Statische Bindung für den Feldzugriff", wenn Sie einen Blick darauf werfen möchten.
Aber warum verhalten sich Methoden anders? Die Antwort ist, dass Java für Methoden late binding verwendet. Das bedeutet, dass es zur Kompilierzeit die geeignetste Methode für search für die Laufzeit findet. Die Suche umfasst den Fall, dass die Methode in einer Klasse überschrieben wird.
Dies wird als Variablenverstecken bezeichnet . Wenn Sie aRef = b;
zuweisen, hat aRef
zwei intVal, 1 heißt nur intVal
, und eine andere ist unter A.intVal
verborgen (siehe Debugger-Screenshot). Da Ihre Variable vom Typ class A
ist, greift Java auch dann auf A.intVal
zu, wenn Sie nur intVal
drucken.
Antwort 1 : Eine Möglichkeit, auf die intVal
einer untergeordneten Klasse zuzugreifen, ist System.out.println((B)aRef.intVal);
Antwort 2 : Eine andere Möglichkeit ist Java Reflection, da Java, wenn Sie Reflection verwenden, versteckten A.intVal
basierend auf dem Klassentyp nicht intelligent erfassen kann, den Variablennamen als Zeichenfolge übernehmen muss -
import Java.lang.reflect.Field;
class A{
public int intVal = 1;
public void identifyClass()
{
System.out.println("I am class A");
}
}
class B extends A
{
public int intVal = 2;
public void identifyClass()
{
System.out.println("I am class B");
}
}
public class Main
{
public static void main(String [] args) throws Exception
{
A a = new A();
B b = new B();
A aRef;
aRef = a;
System.out.println(aRef.intVal);
aRef.identifyClass();
aRef = b;
Field xField = aRef.getClass().getField("intVal");
System.out.println(xField.get(aRef));
aRef.identifyClass();
}
}
Ausgabe -
1
I am class A
2
I am class B
Ich hoffe das kann helfen
public class B extends A {
// public int intVal = 2;
public B() {
super();
super.intVal = 2;
}
public void identifyClass() {
System.out.println("I am class B");
}
}
Gemäß den Java-Spezifikationen werden die Instanzvariablen bei der Erweiterung nicht von einer Unterklasse durch eine Unterklasse überschrieben.
Daher kann nur die Variable in der Unterklasse als eine mit demselben Namen bezeichnet werden.
Auch wenn der Konstruktor von A während der Instanzerstellung von B aufgerufen wird, wird die Variable (intVal) initialisiert und damit die Ausgabe.
Ich hoffe, du hast die Antwort bekommen. Wenn nicht, können Sie versuchen, den Debug-Modus anzuzeigen. Die Unterklasse B hat Zugriff auf beide Werte. Sie sind nicht polymorph und werden daher nicht überschrieben.
Wenn Sie die Referenz von B verwenden, erhalten Sie das IntVal von B. Wenn Sie die Referenz von A verwenden, erhalten Sie das IntVal von A. So einfach ist das.