72

私は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がbintValに設定されているのに、なぜクラスAのままなのか理解できません。

4

13 に答える 13

89

サブクラスで同じ名前の変数を作成する場合、それは非表示と呼ばれます。結果のサブクラスには、実際には両方のプロパティがあります。super.varまたはでスーパークラスからアクセスできます((SuperClass)this).var。変数は同じタイプである必要はありません。これらは、2つのオーバーロードされたメソッドのように、名前を共有する2つの変数にすぎません。

于 2012-05-23T14:32:20.570 に答える
67

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

于 2012-05-23T14:30:58.607 に答える
18

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

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

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

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

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

于 2016-12-29T12:15:43.983 に答える
15

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

于 2012-05-23T14:41:45.430 に答える
4

Java関数のオーバーライドの概念は、オブジェクトタイプに応じてオーバーライドされ、変数は参照型でアクセスされます。

  1. 関数のオーバーライド:この場合、親クラスと子クラスの両方が独自の定義を持つ同じ関数名を持っていると仮定します。ただし、どの関数を実行するかは、実行時の参照型ではなく、オブジェクト型によって異なります。

例:

Parent parent=new Child();
parent.behaviour();

parentこれは親クラスの参照ですが、子クラスのオブジェクトを保持しているため、その場合は子クラス関数が呼び出されます。

Child child=new Child();
child.behaviour();

ここでchildは子クラスのオブジェクトを保持しているため、子クラス関数が呼び出されます。

Parent parent=new Parent();
parent.behaviour();

ここでparentは親クラスのオブジェクトを保持しているため、親クラス関数が呼び出されます。

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

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

例:

Parent parent=new Child();
System.out.println(parent.state);

参照型はParentであるため、Childクラス変数ではなく、Parentクラス変数にアクセスします。

Child child=new Child();
System.out.println(child.state);

ここでの参照型はChildであるため、Parentクラス変数ではなくChildクラス変数にアクセスします。

Parent parent=new Parent();
System.out.println(parent.state);

ここで、参照型はParentであるため、Parentクラス変数にアクセスします。

于 2018-01-22T08:05:54.043 に答える
3

JLS Java SE 7Edition§15.11.1から:

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

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

Javaでは、クラスメンバーは、実際のオブジェクトのタイプではなく、参照のタイプに従ってアクセスされます。同じ理由で、someOtherMethodInB()クラス内にいる場合は、実行後Bにアクセスできなくなります。識別子(つまり、クラス、変数などの名前)はコンパイル時に解決されるため、コンパイラーはこれを行うために参照型に依存します。aRefaRef = b

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

しかし、なぜメソッドの動作が異なるのでしょうか。答えは、メソッドの場合、Javaは遅延バインディングを使用するということです。つまり、コンパイル時に、実行時に検索するのに最適な方法が見つかります。検索には、あるクラスでメソッドがオーバーライドされる場合が含まれます。

于 2016-11-22T22:03:00.743 に答える
2

これは変数の非表示と呼ばれます。を割り当てるaRef = b;と、aRef2つのintValがあり、1は名前が付けられ、intValもう1つは下に隠されA.intValます(デバッガーのスクリーンショットを参照)。変数はタイプclass Aであるため、印刷する場合でも、intValjavaだけをインテリジェントに取得しA.intValます。

回答1:子クラスにアクセスする1つの方法intValSystem.out.println((B)aRef.intVal);

回答2:それを行う別の方法はJavaリフレクションです。リフレクションを使用する場合A.intVal、クラスタイプに基づいて非表示のJavaカントをインテリジェントにピックアップする場合、文字列として指定された変数名をピックアップする必要があります-

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

ここに画像の説明を入力してください

于 2019-04-04T10:37:55.120 に答える
1

これがお役に立てば幸いです。

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

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

于 2018-05-25T02:57:06.970 に答える
0

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

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

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

于 2012-05-23T14:48:13.593 に答える
0

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

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

于 2012-05-23T15:22:40.737 に答える
0

これは、bをaRefに割り当てると解決され、aRefがクラスAになるためです。これは、aRefがクラスBのフィールドまたはメソッドにアクセスできないことを意味します。代わりにb.intValを使用してintValを呼び出すと、2が得られます。

于 2021-01-30T08:04:40.003 に答える
-1

多くのユーザーがすでに指摘しているように、これはポリモーフィズムではありません。ポリモーフィズムはメソッド(関数)にのみ適用されます。

クラスAのintValの値が出力される理由については、参照aRefがタイプAであることがわかるため、これが発生します。

なぜあなたがそれによって混乱しているのか分かります。同じ手順で、exのオーバーライドされたメソッドにアクセスしました。メソッドidentifyClass()ですが、私が書いた最初の行を直接証明する変数ではありません。

これで、変数にアクセスするために((Superclass)c).varを実行できます。

ここで、スーパークラスは、たとえばA<-B<-Cのように多くのレベルを上げることができることに注意してください。つまり、CはBを拡張し、BはAを拡張します。Aのvarの値が必要な場合は、((A)c).varを実行できます。

編集:ユーザーの1人が指摘したように、この「トリック」は静的メソッドには適用されません。静的メソッドだからです。

于 2020-10-26T03:26:32.383 に答える
-2

Javaにはカプセル化の羽があり、オブジェクトのプロパティと動作を緊密にバインドします。したがって、クラス参照を介してのみ、そのプロパティを変更するための動作を呼び出すことができます。

継承では、メソッドのみがオーバーライドされるため、そのプロパティにのみ影響を与えることができます。

于 2016-01-07T13:27:26.127 に答える