Javaでメンバー関数をオーバーライドすることを検討しており、メンバー変数をオーバーライドする実験を検討しました。
そこで、クラスを定義しました
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();
}
}
出力は次のとおりです。
1
I am class A
1
I am class B
ARefがbに設定されている場合、intValがまだクラスAである理由を理解できません。
サブクラスで同じ名前の変数を作成すると、それはhidingと呼ばれます。結果のサブクラスは、実際にはbothプロパティを持ちます。 super.var
または((SuperClass)this).var
を使用して、スーパークラスからアクセスできます。変数は同じ型である必要さえありません。これらは、2つのオーバーロードメソッドのように、名前を共有する2つの変数にすぎません。
Javaの変数は多相ではありません。それらは互いにオーバーライドしません。
変数はコンパイル時に解決され、メソッドは実行時に解決されます。 aRefはタイプAであるため、aRef.Intvalueはコンパイル時に1に解決されます。
Javaのフィールドにはポリモーフィズムはありません。
Variables
の決定はコンパイル時に行われるため、常に基本クラス変数(子の継承変数ではない)にアクセスします。
したがって、アップキャストが発生するときは常に覚えておいてください
1)基本クラス変数にアクセスします。
2)サブクラスメソッド(オーバーライドが発生した場合はオーバーライドされたメソッド、親からそのまま継承されたメソッド)が呼び出されます。
JavaのOverRiding Conceptは、オブジェクトタイプに依存してオーバーライドされ、変数は参照タイプにアクセスします。
例:
Parent parent=new Child();
parent.behaviour();
ここでparent
はParentクラスの参照ですが、Childクラスのオブジェクトを保持しているため、その場合にChildクラス関数が呼び出されます。
Child child=new Child();
child.behaviour();
ここでchild
はChild Classのオブジェクトを保持しているため、Childクラス関数が呼び出されます。
Parent parent=new Parent();
parent.behaviour();
ここでparent
はParent Classのオブジェクトを保持しているため、Parentクラス関数が呼び出されます。
変数にアクセスしようとすると、オブジェクトタイプではなく、参照タイプオブジェクトに依存します。
例:
Parent parent=new Child();
System.out.println(parent.state);
参照タイプは親なので、子クラス変数ではなく、親クラス変数にアクセスします。
Child child=new Child();
System.out.println(child.state);
ここでは、参照タイプは子であるため、親クラス変数ではなく、子クラス変数にアクセスします。
Parent parent=new Parent();
System.out.println(parent.state);
ここでは、参照タイプは親なので、親クラス変数にアクセスします。
JLSからJava SE 7 Edition§15.11.1:
このフィールドアクセスの動的ルックアップの欠如により、簡単な実装でプログラムを効率的に実行できます。遅延バインディングとオーバーライドの機能は利用可能ですが、インスタンスメソッドが使用されている場合のみです。
オリバー・チャールズワースとマルコ・トポリニクの答えは正しいです。質問のwhy部分についてもう少し詳しく説明したいと思います。
In Java クラスメンバ は、実際のオブジェクトのタイプではなく、参照のタイプに従ってaccessedです。同じ理由で、クラスB
にsomeOtherMethodInB()
がある場合、_aRef = b
_が実行された後、aRef
からアクセスすることはできません。 (つまり、クラス、変数などの名前)はコンパイル時に解決されるため、コンパイラはこれを行うために参照型に依存します。
さて、あなたの例では、System.out.println(aRef.intVal);
を実行すると、intVal
で定義されているA
の値を出力します。これは、アクセスに使用する参照のタイプだからです。コンパイラは、aRef
がA
型であり、それがアクセスするintVal
であることを認識します。 B
のインスタンスにbothフィールドがあることを忘れないでください。 JLSには、「15.11.1-1。フィールドアクセス用の静的バインディング」を参照したい場合の例もあります。
しかし、なぜメソッドの動作は異なるのでしょうか?答えは、メソッドの場合、Javaはlate bindingを使用します。つまり、コンパイル時に、最も多くの実行時のsearchに適したメソッド検索には、メソッドが何らかのクラスでオーバーライドされる場合が含まれます。
これが役立つことを願っています:
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");
}
}
したがって、基本クラスの変数をオーバーライドすることはできませんが、継承クラスのコンストラクターから基本クラス変数の値を設定(変更)できます。
これは変数非表示と呼ばれます。 _aRef = b;
_を割り当てると、aRef
には2つのintValがあり、1はちょうどintVal
という名前になり、別の変数は_A.intVal
_の下に隠されます(デバッガのスクリーンショットを参照) _class A
_は、単にintVal
Javaがインテリジェントに_A.intVal
_を取得します。
回答1:子クラスのintVal
にアクセスする1つの方法はSystem.out.println((B)aRef.intVal);
です
Answer 2:それを行う別の方法はJava Reflectionを使用するときの反射Javaクラスタイプに基づいて隠れた_A.intVal
_をインテリジェントにピックアップすることはできません。文字列として指定された変数名をピックアップする必要があります-
_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();
}
}
_
出力-
_1
I am class A
2
I am class B
_
Java仕様に従って、インスタンス変数は、サブクラスが拡張されたときにスーパークラスからサブクラスによってオーバーライドされません。
したがって、サブクラスの変数は、同じ名前を共有する変数としてのみ見ることができます。
また、Bのインスタンス作成中にAのコンストラクターが呼び出されると、変数(intVal)が初期化されるため、出力が生成されます。
まあ、私はあなたが答えを得たことを願っています。そうでない場合は、デバッグモードで確認してみてください。サブクラスBは両方のintValにアクセスできます。それらはポリモーフィックではないため、オーバーライドされません。
Bの参照を使用すると、BのintValが取得されます。 Aのreferenceを使用すると、AのintValが取得されます。とても簡単です。