web-dev-qa-db-ja.com

Java(Variable Hiding)でのメンバー変数のオーバーライド

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である理由を理解できません。

58
Kalyan Raghu

サブクラスで同じ名前の変数を作成すると、それはhidingと呼ばれます。結果のサブクラスは、実際にはbothプロパティを持ちます。 super.varまたは((SuperClass)this).varを使用して、スーパークラスからアクセスできます。変数は同じ型である必要さえありません。これらは、2つのオーバーロードメソッドのように、名前を共有する2つの変数にすぎません。

73
Marko Topolnik

Javaの変数は多相ではありません。それらは互いにオーバーライドしません。

53

変数はコンパイル時に解決され、メソッドは実行時に解決されます。 aRefはタイプAであるため、aRef.Intvalueはコンパイル時に1に解決されます。

11
Rostislav Matl

Javaのフィールドにはポリモーフィズムはありません。

Variablesの決定はコンパイル時に行われるため、常に基本クラス変数(子の継承変数ではない)にアクセスします。

したがって、アップキャストが発生するときは常に覚えておいてください

1)基本クラス変数にアクセスします。

2)サブクラスメソッド(オーバーライドが発生した場合はオーバーライドされたメソッド、親からそのまま継承されたメソッド)が呼び出されます。

10
Ankur Singhal

JavaのOverRiding Conceptは、オブジェクトタイプに依存してオーバーライドされ、変数は参照タイプにアクセスします。

  1. 関数のオーバーライド:この場合、親クラスと子クラスの両方に同じ名前の関数があり、独自の定義があるとします。ただし、実行する関数は、実行時の参照型ではなくオブジェクト型に依存します。

例:

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クラス関数が呼び出されます。

  1. 変数のオーバーライド:Javaはオーバーロードされた変数をサポートします。実際には、これらは同じ名前の2つの異なる変数です。1つは親クラスに、1つは子クラスにあります。両方の変数は同じデータ型または異なる。

変数にアクセスしようとすると、オブジェクトタイプではなく、参照タイプオブジェクトに依存します。

例:

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);

ここでは、参照タイプは親なので、親クラス変数にアクセスします。

4
Aman Goyal

JLSからJava SE 7 Edition§15.11.1:

このフィールドアクセスの動的ルックアップの欠如により、簡単な実装でプログラムを効率的に実行できます。遅延バインディングとオーバーライドの機能は利用可能ですが、インスタンスメソッドが使用されている場合のみです。

オリバー・チャールズワースとマルコ・トポリニクの答えは正しいです。質問のwhy部分についてもう少し詳しく説明したいと思います。

In Java クラスメンバ は、実際のオブジェクトのタイプではなく、参照のタイプに従ってaccessedです。同じ理由で、クラスBsomeOtherMethodInB()がある場合、_aRef = b_が実行された後、aRefからアクセスすることはできません。 (つまり、クラス、変数などの名前)はコンパイル時に解決されるため、コンパイラはこれを行うために参照型に依存します。

さて、あなたの例では、System.out.println(aRef.intVal);を実行すると、intValで定義されているAの値を出力します。これは、アクセスに使用する参照のタイプだからです。コンパイラは、aRefA型であり、それがアクセスするintValであることを認識します。 Bのインスタンスにbothフィールドがあることを忘れないでください。 JLSには、「15.11.1-1。フィールドアクセス用の静的バインディング」を参照したい場合の例もあります。

しかし、なぜメソッドの動作は異なるのでしょうか?答えは、メソッドの場合、Javaはlate bindingを使用します。つまり、コンパイル時に、最も多くの実行時のsearchに適したメソッド検索には、メソッドが何らかのクラスでオーバーライドされる場合が含まれます。

3

これが役立つことを願っています:

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");
    }
}

したがって、基本クラスの変数をオーバーライドすることはできませんが、継承クラスのコンストラクターから基本クラス変数の値を設定(変更)できます。

1

これは変数非表示と呼ばれます。 _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
_

enter image description here

1
sapy

Java仕様に従って、インスタンス変数は、サブクラスが拡張されたときにスーパークラスからサブクラスによってオーバーライドされません。

したがって、サブクラスの変数は、同じ名前を共有する変数としてのみ見ることができます。

また、Bのインスタンス作成中にAのコンストラクターが呼び出されると、変数(intVal)が初期化されるため、出力が生成されます。

0
Siddharth Singh

まあ、私はあなたが答えを得たことを願っています。そうでない場合は、デバッグモードで確認してみてください。サブクラスBは両方のintValにアクセスできます。それらはポリモーフィックではないため、オーバーライドされません。

Bの参照を使用すると、BのintValが取得されます。 Aのreferenceを使用すると、AのintValが取得されます。とても簡単です。

0
dharam