web-dev-qa-db-ja.com

Java:オーバーライドされたメソッドを呼び出すスーパーメソッドの呼び出し

public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.method2();
    }

    public void method2()
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

私の期待される出力:

サブクラスmethod1
スーパークラスmethod1
スーパークラスmethod2

実際の出力:

サブクラスmethod1
スーパークラスmethod1
サブクラスmethod2

技術的にはパブリックメソッドをオーバーライドしたことは知っていますが、スーパーを呼び出していたため、スーパー内の呼び出しはすべてスーパー内にとどまり、これは起こらないと考えました。どうすればそれを実現できるかについてのアイデアはありますか?

82
jsonfry

キーワードsuperは「固執」しません。すべてのメソッド呼び出しは個別に処理されるため、superを呼び出してSuperClass.method1()に到達した場合でも、将来行う他のメソッド呼び出しには影響しません。

つまり、SuperClassの実際のインスタンスで作業している場合を除き、SuperClass.method2()を経由せずにSuperClass.method1()からSubClass.method2()を呼び出す直接的な方法はありません。 。

Reflectionを使用して目的の効果を達成することさえできません( Java.lang.reflect.Method.invoke(Object, Object...)のドキュメントを参照 )。

[編集]まだ混乱があるようです。別の説明を試してみましょう。

foo()を呼び出すと、実際にはthis.foo()を呼び出します。 Javaでは、単純にthisを省略できます。質問の例では、thisのタイプはSubClassです。

JavaがSuperClass.method1()のコードを実行すると、最終的にthis.method2();に到達します。

superを使用しても、thisが指すインスタンスは変更されません。 thisSubClass型なので、呼び出しはSubClass.method2()になります。

Javaがthisを非表示の最初のパラメーターとして渡すと想像すると理解しやすいかもしれません。

_public class SuperClass
{
    public void method1(SuperClass this)
    {
        System.out.println("superclass method1");
        this.method2(this); // <--- this == mSubClass
    }

    public void method2(SuperClass this)
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1(SubClass this)
    {
        System.out.println("subclass method1");
        super.method1(this);
    }

    @Override
    public void method2(SubClass this)
    {
        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1(mSubClass);
    }
}
_

呼び出しスタックをたどると、thisが変更されることはなく、常にmain()で作成されたインスタンスであることがわかります。

68
Aaron Digulla

オーバーライドメソッド(またはオーバーライドクラスの他のメソッド)でのみオーバーライドされたメソッドにアクセスできます。

したがって:method2()をオーバーライドしないか、オーバーライドされたバージョン内でsuper.method2()を呼び出します。

12

「使用しているオブジェクトの現在実行中のインスタンス」を実際に参照するthisキーワードを使用している、つまり、スーパークラスでthis.method2();を呼び出している、つまり、使用しているオブジェクト、つまりサブクラスでmethod2()を呼び出します。

8
Jose Diaz

このように考えます

+----------------+
|     super      |
+----------------+ <-----------------+
| +------------+ |                   |
| |    this    | | <-+               |
| +------------+ |   |               |
| | @method1() | |   |               |
| | @method2() | |   |               |
| +------------+ |   |               |
|    method4()   |   |               |
|    method5()   |   |               |
+----------------+   |               |
    We instantiate that class, not that one!

そのサブクラスを少し左に移動して、下にあるものを明らかにします...(男、私はASCIIグラフィックスが大好きです)

We are here
        |
       /  +----------------+
      |   |     super      |
      v   +----------------+
+------------+             |
|    this    |             |
+------------+             |
| @method1() | method1()   |
| @method2() | method2()   |
+------------+ method3()   |
          |    method4()   |
          |    method5()   |
          +----------------+

Then we call the method
over here...
      |               +----------------+
 _____/               |     super      |
/                     +----------------+
|   +------------+    |    bar()       |
|   |    this    |    |    foo()       |
|   +------------+    |    method0()   |
+-> | @method1() |--->|    method1()   | <------------------------------+
    | @method2() | ^  |    method2()   |                                |
    +------------+ |  |    method3()   |                                |
                   |  |    method4()   |                                |
                   |  |    method5()   |                                |
                   |  +----------------+                                |
                   \______________________________________              |
                                                          \             |
                                                          |             |
...which calls super, thus calling the super's method1() here, so that that
method (the overidden one) is executed instead[of the overriding one].

Keep in mind that, in the inheritance hierarchy, since the instantiated
class is the sub one, for methods called via super.something() everything
is the same except for one thing (two, actually): "this" means "the only
this we have" (a pointer to the class we have instantiated, the
subclass), even when Java syntax allows us to omit "this" (most of the
time); "super", though, is polymorphism-aware and always refers to the
superclass of the class (instantiated or not) that we're actually
executing code from ("this" is about objects [and can't be used in a
static context], super is about classes).

つまり、 Java言語仕様 からの引用:

フォーム super.Identifierは、現在のオブジェクトのIdentifierという名前のフィールドを参照しますが、現在のオブジェクトは現在のクラスのスーパークラスのインスタンスとして表示されます。

フォーム T.super.Identifierは、Identifierに対応する字句的に囲むインスタンスのTという名前のフィールドを指しますが、そのインスタンスはTのスーパークラスのインスタンスとして表示されます。

素人の言い方をすれば、thisは基本的にオブジェクト(* the **オブジェクト。変数内を移動できる同じオブジェクト)、インスタンス化されたクラスのインスタンス、データドメインのプレーン変数です。 superは、実行したいコードの借用ブロックへのポインタのようなもので、単なる関数呼び出しのようなものであり、呼び出されるクラスに関連しています。

したがって、スーパークラスからsuperを使用すると、スーパーデュパークラス[祖父母]からコードが取得されます)、スーパークラスからthis(または暗黙的に使用される)を使用すると、サブクラスに(誰も変更していないため-そして誰も変更できなかったため)。

4
Unai Vivi

オーバーライドされるメソッドを回避する唯一の方法は、キーワードsuperを使用することなので、method2()をSuperClassから上に移動することを考えました別の新しいBaseクラスに追加し、SuperClassから呼び出します。

class Base 
{
    public void method2()
    {
        System.out.println("superclass method2");
    }
}

class SuperClass extends Base
{
    public void method1()
    {
        System.out.println("superclass method1");
        super.method2();
    }
}

class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}

public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

出力:

subclass method1
superclass method1
superclass method2
2
carlos_lm
    class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        SuperClass se=new SuperClass();
        se.method2();

    }

    public void method2()
    {
        System.out.println("superclass method2");
    }

}



 class SubClass extends SuperClass
{
    @Override

    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();



    }

    @Override

    public void method2()
    {

        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

出力:

サブクラスmethod1
スーパークラスmethod1
スーパークラスmethod2

2
Vijay

SuperClass.method1がsubClass.method2を呼び出さないようにするには、オーバーライドできないようにmethod2をプライベートにします。

提案は次のとおりです。

public class SuperClass {

  public void method1() {
    System.out.println("superclass method1");
    this.internalMethod2();
  }

  public void method2()  {
    // this method can be overridden.  
    // It can still be invoked by a childclass using super
    internalMethod2();
  }

  private void internalMethod2()  {
    // this one cannot.  Call this one if you want to be sure to use
    // this implementation.
    System.out.println("superclass method2");
  }

}

public class SubClass extends SuperClass {

  @Override
  public void method1() {
    System.out.println("subclass method1");
    super.method1();
  }

  @Override
  public void method2() {
    System.out.println("subclass method2");
  }
}

この方法で機能しなかった場合、ポリモーフィズムは不可能(または少なくとも半分ほど有用ではない)になります。

2
Joeri Hendrickx

thisは常に現在実行中のオブジェクトを指します。

ここでポイントをさらに説明するために、簡単なスケッチを示します。

+----------------+
|  Subclass      |
|----------------|
|  @method1()    |
|  @method2()    |
|                |
| +------------+ |
| | Superclass | |
| |------------| |
| | method1()  | |
| | method2()  | |
| +------------+ |
+----------------+

外部ボックスのインスタンスであるSubclassオブジェクトがある場合は、ボックス内でSuperclass 'area'にいたとしても、それは依然として外部ボックスのインスタンスです。 。

さらに、このプログラムには3つのクラスから作成されるオブジェクトが1つしかないため、thisは1つのものしか参照できず、次のようになります。

enter image description here

Netbeans 'Heap Walker'に示すように。

2
Johnny Baloney

要約すると、これは現在のオブジェクトを指し、Javaのメソッド呼び出しは本質的にポリモーフィックです。したがって、実行のためのメソッド選択は、これが指すオブジェクトに完全に依存します。 thisが子クラスのオブジェクトを指しているため、親クラスから子クラスのmethod2()を呼び出します。

PS。メソッドとは異なり、クラスのメンバー変数はポリモーフィックではありません。

「this」キーワードは、現在のクラス参照を指します。つまり、メソッド内で使用される場合、「現在の」クラスは依然としてサブクラスであるため、答えが説明されます。

1
Ozil

直接できるとは思いません。 1つの回避策は、スーパークラスにmethod2のプライベート内部実装を持ち、それを呼び出すことです。例えば:

public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.internalMethod2();
    }

    public void method2()
    {
        this.internalMethod2(); 
    }
    private void internalMethod2()
    {
        System.out.println("superclass method2");
    }

}
1
David Gelhar

同様のケースの調査中に、サブクラスメソッドのスタックトレースをチェックして、呼び出し元の場所を確認しました。おそらくもっと賢い方法がありますが、私にとってはうまくいき、動的なアプローチです。

public void method2(){
        Exception ex=new Exception();
        StackTraceElement[] ste=ex.getStackTrace();
        if(ste[1].getClassName().equals(this.getClass().getSuperclass().getName())){
            super.method2();
        }
        else{
            //subclass method2 code
        }
}

ケースの解決策があるという質問は合理的だと思います。もちろん、スレッドで既に述べたように、異なるメソッド名または異なるパラメータータイプの問題を解決する方法はありますが、私の場合、異なるメソッド名で混乱するのは好きではありませんでした。

0
Beat Siegrist