it-swarm.com.de

Java: Aufrufen einer Supermethode, die eine überschriebene Methode aufruft

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

meine erwartete Ausgabe:

unterklasse method1
Superklasse Methode1
Superklasse Methode2 

tatsächliche Leistung:

unterklasse method1
Superklasse Methode1
Unterklasse method2

Ich weiß, dass ich technisch eine öffentliche Methode außer Kraft gesetzt habe, aber ich dachte mir, dass alle Anrufe innerhalb des Supers im Super bleiben würden, da ich das Super anrufe. Irgendwelche Ideen, wie ich es schaffen kann?

76
jsonfry

Das Schlüsselwort super "klebt" nicht. Jeder Methodenaufruf wird einzeln behandelt. Selbst wenn Sie SuperClass.method1() aufgerufen haben, indem Sie super aufrufen, hat dies keinen Einfluss auf andere Methodenaufrufe, die Sie möglicherweise in der Zukunft ausführen.

Das bedeutet, dass es keine direkte Möglichkeit gibt, SuperClass.method2() von SuperClass.method1() aufzurufen, ohne SubClass.method2() zu verwenden, es sei denn, Sie arbeiten mit einer tatsächlichen Instanz von SuperClass.

Sie können mit Reflection nicht einmal den gewünschten Effekt erzielen (siehe Dokumentation von Java.lang.reflect.Method.invoke(Object, Object...) ).

[EDIT] Es scheint immer noch einige Verwirrung zu geben. Lassen Sie mich eine andere Erklärung versuchen.

Wenn Sie foo() aufrufen, rufen Sie tatsächlich this.foo() auf. Java lässt Sie einfach die this weg. In dem Beispiel in der Frage ist this der Typ von SubClass.

Wenn Java also den Code in SuperClass.method1() ausführt, kommt es schließlich zu this.method2();.

Die Verwendung von super ändert nicht die Instanz, auf die this zeigt. Der Aufruf geht also an SubClass.method2(), da this vom Typ SubClass ist.

Vielleicht ist es einfacher zu verstehen, wenn Sie sich vorstellen, dass Java this als versteckten ersten Parameter übergibt:

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

Wenn Sie dem Aufrufstapel folgen, können Sie feststellen, dass this sich nie ändert. Es ist immer die in main() erstellte Instanz.

65
Aaron Digulla

Sie können nur auf überschriebene Methoden in den überschreibenden Methoden (oder in anderen Methoden der überschreibenden Klasse) zugreifen.

Also: entweder method2() nicht überschreiben oder super.method2() in der überschriebenen Version aufrufen.

12

Sie verwenden das Schlüsselwort this, das sich tatsächlich auf die "aktuell ausgeführte Instanz des von Ihnen verwendeten Objekts" bezieht, das heißt, Sie rufen this.method2(); in Ihrer Superklasse auf, d Sie verwenden die SubClass.

8
Jose Diaz

Da die einzige Möglichkeit, eine Methode zum Überschreiben zu vermeiden, die Verwendung des Schlüsselworts super ist, habe ich gedacht, die Methode2 () von SuperClass auf eine andere neue Base - Klasse zu verschieben und dann nenne es von 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();
    }
}

Ausgabe:

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

Ausgabe:

unterklasse method1
Superklasse Methode1
Superklasse Methode2

2
Vijay

Wenn Sie nicht möchten, dass superClass.method1 subClass.method2 aufruft, machen Sie method2 privat, damit sie nicht überschrieben werden kann.

Hier ist ein Vorschlag:

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

Wenn es nicht so funktionieren würde, wäre Polymorphismus unmöglich (oder zumindest nicht halb so nützlich).

2
Joeri Hendrickx

this bezieht sich immer auf das aktuell ausgeführte Objekt.

Um den Punkt weiter zu veranschaulichen, ist hier eine einfache Skizze:

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

Wenn Sie eine Instanz der äußeren Box, ein Subclass-Objekt, haben, wo immer Sie gerade in die Box hineinfahren, sogar in den Bereich Superclass, ist dies immer noch die Instanz der äußeren Box.

Darüber hinaus gibt es in diesem Programm nur ein Objekt, das aus den drei Klassen erstellt wird. this kann sich immer nur auf eine Sache beziehen.

 enter image description here

wie im Netbeans 'Heap Walker' gezeigt.

2
Johnny Baloney

Zusammenfassend weist dies auf das aktuelle Objekt hin und der Aufruf der Methode in Java ist von Natur aus polymorph. Daher hängt die Auswahl der Methode für die Ausführung vollständig von dem Objekt ab, auf das darauf hingewiesen wird. Daher ruft der Aufruf der Methode method2 () von der übergeordneten Klasse method2 () der untergeordneten Klasse auf, da dies auf das Objekt der untergeordneten Klasse verweist. Die Definition dieser Einstellung ändert sich nicht, unabhängig von der verwendeten Klasse.

PS. Im Gegensatz zu Methoden sind Membervariablen der Klasse nicht polymorph.

"dieses" Schlüsselwort bezieht sich auf die aktuelle Klassenreferenz. Das heißt, wenn es innerhalb der Methode verwendet wird, ist die 'aktuelle' Klasse immer noch SubClass und die Antwort wird erklärt.

1
Ozil

Ich denke so darüber nach

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

Lassen Sie mich diese Unterklasse etwas nach links verschieben, um herauszufinden, was sich darunter befindet ... (Mann, ich liebe ASCII - Grafik).

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

Mit anderen Worten, Zitate aus der Java-Sprachspezifikation :

Das Formular super.Identifier bezieht sich auf das Feld Identifier der aktuelles Objekt, jedoch mit dem aktuellen Objekt als Instanz von die Oberklasse der aktuellen Klasse.

Das Formular T.super.Identifier bezieht sich auf das Feld Identifier von die lexikalisch einschließende Instanz, die T entspricht, aber damit Instanz, die als Instanz der Oberklasse von T angesehen wird.

In der Sprache der Laien ist this im Grunde ein Objekt (* das ** Objekt; das gleiche Objekt, das Sie in Variablen verschieben können), die Instanz der instanziierten Klasse, eine einfache Variable in der Datendomäne. super ist wie ein Zeiger auf einen geliehenen Codeblock, den Sie ausführen möchten, eher wie ein reiner Funktionsaufruf und relativ zu der Klasse, in der er aufgerufen wird.

Wenn Sie also super aus der Superklasse verwenden, erhalten Sie Code aus der Superdup-Klasse [der Großelternteil] ausgeführt. Wenn Sie jedoch this (oder wenn sie implizit verwendet wird) aus einer Superklasse verwenden, zeigt sie immer auf die Unterklasse (da niemand sie geändert hat.) und niemand konnte).

1
Unai Vivi

Ich glaube nicht, dass du es direkt machen kannst. Eine Problemumgehung wäre, eine private interne Implementierung von Methode2 in der Superklasse zu haben und diese aufzurufen. Zum Beispiel:

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

Während meiner Nachforschungen für einen ähnlichen Fall habe ich am Ende den Stack-Trace in der Unterklassenmethode überprüft, um herauszufinden, woher der Anruf kommt. Es gibt wahrscheinlich intelligentere Wege, dies zu tun, aber es funktioniert für mich und es ist ein dynamischer Ansatz.

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
        }
}

Ich denke, die Frage nach einer Lösung für den Fall ist vernünftig. Es gibt natürlich Möglichkeiten, das Problem mit verschiedenen Methodennamen oder sogar verschiedenen Parametertypen zu lösen, wie bereits im Thread erwähnt. In meinem Fall möchte ich jedoch nicht durch verschiedene Methodennamen verwechseln.

0
Beat Siegrist